/*
    Nintendo 64 Video Hardware

    Written by Ville Linde
*/

// #include "driver.h"
// #include "includes/n64.h"
#include "Gfx #1.3.h"
#include "z64.h"
#include <stdlib.h>
#include <stdio.h>

inline void fatalerror(const char * err, ...)
{
  fprintf(stderr, "%s\n", err);
  //exit(0);
}

#define INLINE inline

#define LOG_RDP_EXECUTION 		0

#if LOG_RDP_EXECUTION
static FILE *rdp_exec;
#endif

/* defined in systems/n64.c */
#define rdram ((UINT32*)gfx.RDRAM)
//extern UINT32 *rdram;
#define rsp_imem ((UINT32*)gfx.IMEM)
//extern UINT32 *rsp_imem;
#define rsp_dmem ((UINT32*)gfx.DMEM)
//extern UINT32 *rsp_dmem;
//extern void dp_full_sync(void);

#define vi_origin (*(UINT32*)gfx.VI_ORIGIN_REG)
//extern UINT32 vi_origin;
#define vi_width (*(UINT32*)gfx.VI_WIDTH_REG)
//extern UINT32 vi_width;
#define vi_control (*(UINT32*)gfx.VI_STATUS_REG)
//extern UINT32 vi_control;

#define dp_start (*(UINT32*)gfx.DPC_START_REG)
//extern UINT32 dp_start;
#define dp_end (*(UINT32*)gfx.DPC_END_REG)
//extern UINT32 dp_end;
#define dp_current (*(UINT32*)gfx.DPC_CURRENT_REG)
//extern UINT32 dp_current;
#define dp_status (*(UINT32*)gfx.DPC_STATUS_REG)
//extern UINT32 dp_status;

static UINT32 rdp_cmd_data[0x1000];
static int rdp_cmd_ptr = 0;
static int rdp_cmd_cur = 0;

typedef struct
{
	int lx, rx;
	int s, ds;
	int t, dt;
	int w, dw;
	int r, dr;
	int g, dg;
	int b, db;
	int a, da;
	int z, dz;
} SPAN;

static SPAN span[1024];

/*****************************************************************************/

typedef struct
{
	UINT8 r, g, b, a;
} COLOR;

typedef struct
{
	UINT16 xl, yl, xh, yh;		// 10.2 fixed-point
} RECTANGLE;

typedef struct
{
	int tilenum;
	UINT16 xl, yl, xh, yh;		// 10.2 fixed-point
	INT16 s, t;					// 10.5 fixed-point
	INT16 dsdx, dtdy;			// 5.10 fixed-point
} TEX_RECTANGLE;

typedef struct
{
	int format, size;
	UINT32 line;
	UINT32 tmem;
	int palette;
	// TODO: clamp & mirror parameters
	int ct, mt, cs, ms;
	int mask_t, shift_t, mask_s, shift_s;

	UINT16 sl, tl, sh, th;		// 10.2 fixed-point
} TILE;

typedef struct
{
	int sub_a_rgb0;
	int sub_b_rgb0;
	int mul_rgb0;
	int add_rgb0;
	int sub_a_a0;
	int sub_b_a0;
	int mul_a0;
	int add_a0;

	int sub_a_rgb1;
	int sub_b_rgb1;
	int mul_rgb1;
	int add_rgb1;
	int sub_a_a1;
	int sub_b_a1;
	int mul_a1;
	int add_a1;
} COMBINE_MODES;

typedef struct
{
	int cycle_type;
	int persp_tex_en;
	int detail_tex_en;
	int sharpen_tex_en;
	int tex_lod_en;
	int en_tlut;
	int tlut_type;
	int sample_type;
	int mid_texel;
	int bi_lerp0;
	int bi_lerp1;
	int convert_one;
	int key_en;
	int rgb_dither_sel;
	int alpha_dither_sel;
	int blend_m1a_0;
	int blend_m1a_1;
	int blend_m1b_0;
	int blend_m1b_1;
	int blend_m2a_0;
	int blend_m2a_1;
	int blend_m2b_0;
	int blend_m2b_1;
	int force_blend;
	int alpha_cvg_select;
	int cvg_times_alpha;
	int z_mode;
	int cvg_dest;
	int color_on_cvg;
	int image_read_en;
	int z_update_en;
	int z_compare_en;
	int antialias_en;
	int z_source_sel;
	int dither_alpha_en;
	int alpha_compare_en;
} OTHER_MODES;

/*****************************************************************************/

#define PIXEL_SIZE_4BIT			0
#define PIXEL_SIZE_8BIT			1
#define PIXEL_SIZE_16BIT		2
#define PIXEL_SIZE_32BIT		3

#define CYCLE_TYPE_1			0
#define CYCLE_TYPE_2			1
#define CYCLE_TYPE_COPY			2
#define CYCLE_TYPE_FILL			3

static COMBINE_MODES combine;
static OTHER_MODES other_modes;

static COLOR blend_color;
static COLOR prim_color;
static COLOR env_color;
static COLOR fog_color;
static COLOR combined_color;
static COLOR texel0_color;
static COLOR texel1_color;
static COLOR shade_color;

static COLOR one_color		= { 0xff, 0xff, 0xff, 0xff };
static COLOR zero_color		= { 0x00, 0x00, 0x00, 0x00 };

// combiner inputs
static UINT8 *combiner_rgbsub_a_r[2];
static UINT8 *combiner_rgbsub_a_g[2];
static UINT8 *combiner_rgbsub_a_b[2];
static UINT8 *combiner_rgbsub_b_r[2];
static UINT8 *combiner_rgbsub_b_g[2];
static UINT8 *combiner_rgbsub_b_b[2];
static UINT8 *combiner_rgbmul_r[2];
static UINT8 *combiner_rgbmul_g[2];
static UINT8 *combiner_rgbmul_b[2];
static UINT8 *combiner_rgbadd_r[2];
static UINT8 *combiner_rgbadd_g[2];
static UINT8 *combiner_rgbadd_b[2];

static UINT8 *combiner_alphasub_a[2];
static UINT8 *combiner_alphasub_b[2];
static UINT8 *combiner_alphamul[2];
static UINT8 *combiner_alphaadd[2];

// blender input
static UINT8 *blender1a_r[2];
static UINT8 *blender1a_g[2];
static UINT8 *blender1a_b[2];
static UINT8 *blender1b_a[2];
static UINT8 *blender2a_r[2];
static UINT8 *blender2a_g[2];
static UINT8 *blender2a_b[2];
static UINT8 *blender2b_a[2];


static COLOR pixel_color;
static COLOR inv_pixel_color;
static COLOR blended_pixel_color;
static COLOR memory_color;



static UINT32 fill_color;		// packed 16-bit or 32-bit, depending on framebuffer format

static UINT16 primitive_z;
static UINT16 primitive_delta_z;

static int fb_format;
static int fb_size;
static int fb_width;
static UINT32 fb_address;

static UINT32 zb_address;

static int ti_format;
static int ti_size;
static int ti_width;
static UINT32 ti_address;

static TILE tile[8];

static RECTANGLE clip;

static UINT8 texture_cache[0x100000]; // *texture_cache;
static UINT32 tlut[256];

#define CLAMP(S, T)														\
do																		\
{																		\
	if (clamp_s)														\
	{																	\
		if (S < (tsl >> 2)) S = (tsl >> 2);								\
		if (S > (tsh >> 2)) S = (tsh >> 2);								\
	}																	\
	if (mirror_s)														\
	{																	\
		if (S < (tsl >> 2)) S = abs(S - (tsl >> 2));					\
		if (S > (tsh >> 2)) S = abs(S - (tsh >> 2));					\
	}																	\
	if (mask_s != 0)													\
	{																	\
		S &= (((UINT32)(0xffffffff) >> (32-mask_s)));					\
	}																	\
	if (clamp_t)														\
	{																	\
		if (T < (ttl >> 2)) T = (ttl >> 2);								\
		if (T > (tth >> 2)) T = (tth >> 2);								\
	}																	\
	if (mirror_t)														\
	{																	\
		if (T < (ttl >> 2)) T = abs(T - (ttl >> 2));					\
		if (T > (tth >> 2)) T = abs(T - (tth >> 2));					\
	}																	\
	if (mask_t != 0)													\
	{																	\
		T &= (((UINT32)(0xffffffff) >> (32-mask_t)));					\
	}																	\
}																		\
while (0)

/*****************************************************************************/



int rdp_init()
{
#if LOG_RDP_EXECUTION
	rdp_exec = fopen("rdp_execute.txt", "wt");
#endif

	//texture_cache = auto_malloc(0x100000);

	combiner_rgbsub_a_r[0] = combiner_rgbsub_a_r[1] = &one_color.r;
	combiner_rgbsub_a_g[0] = combiner_rgbsub_a_g[1] = &one_color.g;
	combiner_rgbsub_a_b[0] = combiner_rgbsub_a_b[1] = &one_color.b;
	combiner_rgbsub_b_r[0] = combiner_rgbsub_b_r[1] = &one_color.r;
	combiner_rgbsub_b_g[0] = combiner_rgbsub_b_g[1] = &one_color.g;
	combiner_rgbsub_b_b[0] = combiner_rgbsub_b_b[1] = &one_color.b;
	combiner_rgbmul_r[0] = combiner_rgbmul_r[1] = &one_color.r;
	combiner_rgbmul_g[0] = combiner_rgbmul_g[1] = &one_color.g;
	combiner_rgbmul_b[0] = combiner_rgbmul_b[1] = &one_color.b;
	combiner_rgbadd_r[0] = combiner_rgbadd_r[1] = &one_color.r;
	combiner_rgbadd_g[0] = combiner_rgbadd_g[1] = &one_color.g;
	combiner_rgbadd_b[0] = combiner_rgbadd_b[1] = &one_color.b;

	combiner_alphasub_a[0] = combiner_alphasub_a[1] = &one_color.a;
	combiner_alphasub_b[0] = combiner_alphasub_b[1] = &one_color.a;
	combiner_alphamul[0] = combiner_alphamul[1] = &one_color.a;
	combiner_alphaadd[0] = combiner_alphaadd[1] = &one_color.a;

	blender1a_r[0] = blender1a_r[1] = &pixel_color.r;
	blender1a_g[0] = blender1a_g[1] = &pixel_color.r;
	blender1a_b[0] = blender1a_b[1] = &pixel_color.r;
	blender1b_a[0] = blender1b_a[1] = &pixel_color.r;
	blender2a_r[0] = blender2a_r[1] = &pixel_color.r;
	blender2a_g[0] = blender2a_g[1] = &pixel_color.r;
	blender2a_b[0] = blender2a_b[1] = &pixel_color.r;
	blender2b_a[0] = blender2b_a[1] = &pixel_color.r;
	return 0;
}

int rdp_update()
{
	int i, j;
	int height = (vi_control & 0x40) ? 479 : 239;

  static UINT32 old_vi_origin;

  if (old_vi_origin == vi_origin) {
    printf("same\n");
    //return 0;
  }
  old_vi_origin = vi_origin;

  SDL_LockSurface(sdl_Screen);

	switch (vi_control & 0x3)
	{
		case 0:		// blank/no signal
		{
			break;
		}

		case 2:		// RGBA5551
		{
			UINT16 *frame_buffer = (UINT16*)&rdram[vi_origin / 4];
			if (frame_buffer)
			{
				for (j=0; j <height; j++)
				{
					//UINT32 *d = BITMAP_ADDR32(bitmap, j, 0);
          UINT32 * d = (UINT32 *) ((char*)sdl_Screen->pixels + j*sdl_Screen->pitch);
					for (i=0; i < fb_width; i++)
					{
						UINT16 pix = *frame_buffer++;
						int r = ((pix >> 11) & 0x1f) << 3;
						int g = ((pix >> 6) & 0x1f) << 3;
						int b = ((pix >> 1) & 0x1f) << 3;
						d[i^1] = (r << 16) | (g << 8) | b;
					}
				}
			}
			break;
		}

		case 3:		// RGBA8888
		{
			UINT32 *frame_buffer = (UINT32*)&rdram[vi_origin / 4];
			if (frame_buffer)
			{
				for (j=0; j < height; j++)
				{
					//UINT32 *d = BITMAP_ADDR32(bitmap, j, 0);
          UINT32 * d = (UINT32 *) ((char*)sdl_Screen->pixels + j*sdl_Screen->pitch);
					for (i=0; i < fb_width; i++)
					{
						UINT32 pix = *frame_buffer++;
						*d++ = pix & 0xffffff;
					}
				}
			}
			break;
		}

		default:	fatalerror("Unknown framebuffer format %d\n", vi_control & 0x3);
	}

  SDL_UnlockSurface(sdl_Screen);
  SDL_Flip(sdl_Screen);
  //SDL_GL_SwapBuffers();
  
	return 0;
}

/*****************************************************************************/

INLINE void SET_SUBA_RGB_INPUT(UINT8 **input_r, UINT8 **input_g, UINT8 **input_b, int code)
{
	switch (code & 0xf)
	{
		case 0:		*input_r = &combined_color.r;	*input_g = &combined_color.g;	*input_b = &combined_color.b;	break;
		case 1:		*input_r = &texel0_color.r;		*input_g = &texel0_color.g;		*input_b = &texel0_color.b;		break;
		case 2:		*input_r = &texel1_color.r;		*input_g = &texel1_color.g;		*input_b = &texel1_color.b;		break;
		case 3:		*input_r = &prim_color.r;		*input_g = &prim_color.g;		*input_b = &prim_color.b;		break;
		case 4:		*input_r = &shade_color.r;		*input_g = &shade_color.g;		*input_b = &shade_color.b;		break;
		case 5:		*input_r = &env_color.r;		*input_g = &env_color.g;		*input_b = &env_color.b;		break;
		case 6:		*input_r = &one_color.r;		*input_g = &one_color.g;		*input_b = &one_color.b;		break;
		case 7:		break; //TODO fatalerror("SET_SUBA_RGB_INPUT: noise\n"); break;
		case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
		{
			*input_r = &zero_color.r;		*input_g = &zero_color.g;		*input_b = &zero_color.b;		break;
		}
	}
}

INLINE void SET_SUBB_RGB_INPUT(UINT8 **input_r, UINT8 **input_g, UINT8 **input_b, int code)
{
	switch (code & 0xf)
	{
		case 0:		*input_r = &combined_color.r;	*input_g = &combined_color.g;	*input_b = &combined_color.b;	break;
		case 1:		*input_r = &texel0_color.r;		*input_g = &texel0_color.g;		*input_b = &texel0_color.b;		break;
		case 2:		*input_r = &texel1_color.r;		*input_g = &texel1_color.g;		*input_b = &texel1_color.b;		break;
		case 3:		*input_r = &prim_color.r;		*input_g = &prim_color.g;		*input_b = &prim_color.b;		break;
		case 4:		*input_r = &shade_color.r;		*input_g = &shade_color.g;		*input_b = &shade_color.b;		break;
		case 5:		*input_r = &env_color.r;		*input_g = &env_color.g;		*input_b = &env_color.b;		break;
		case 6:		fatalerror("SET_SUBB_RGB_INPUT: key_center\n"); break;
		case 7:		fatalerror("SET_SUBB_RGB_INPUT: convert_k4\n"); break;
		case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
		{
			*input_r = &zero_color.r;		*input_g = &zero_color.g;		*input_b = &zero_color.b;		break;
		}
	}
}

INLINE void SET_MUL_RGB_INPUT(UINT8 **input_r, UINT8 **input_g, UINT8 **input_b, int code)
{
	switch (code & 0x1f)
	{
		case 0:		*input_r = &combined_color.r;	*input_g = &combined_color.g;	*input_b = &combined_color.b;	break;
		case 1:		*input_r = &texel0_color.r;		*input_g = &texel0_color.g;		*input_b = &texel0_color.b;		break;
		case 2:		*input_r = &texel1_color.r;		*input_g = &texel1_color.g;		*input_b = &texel1_color.b;		break;
		case 3:		*input_r = &prim_color.r;		*input_g = &prim_color.g;		*input_b = &prim_color.b;		break;
		case 4:		*input_r = &shade_color.r;		*input_g = &shade_color.g;		*input_b = &shade_color.b;		break;
		case 5:		*input_r = &env_color.r;		*input_g = &env_color.g;		*input_b = &env_color.b;		break;
		case 6:		fatalerror("SET_MUL_RGB_INPUT: key scale\n"); break;
		case 7:		*input_r = &combined_color.a;	*input_g = &combined_color.a;	*input_b = &combined_color.a;	break;
		case 8:		*input_r = &texel0_color.a;		*input_g = &texel0_color.a;		*input_b = &texel0_color.a;		break;
		case 9:		*input_r = &texel1_color.a;		*input_g = &texel1_color.a;		*input_b = &texel1_color.a;		break;
		case 10:	*input_r = &prim_color.a;		*input_g = &prim_color.a;		*input_b = &prim_color.a;		break;
		case 11:	*input_r = &shade_color.a;		*input_g = &shade_color.a;		*input_b = &shade_color.a;		break;
		case 12:	*input_r = &env_color.a;		*input_g = &env_color.a;		*input_b = &env_color.a;		break;
		case 13:	break;//TODO fatalerror("SET_MUL_RGB_INPUT: lod fraction\n"); break;
		case 14:	break;//TODO fatalerror("SET_MUL_RGB_INPUT: primitive lod fraction\n"); break;
		case 15:	break;//TODO fatalerror("SET_MUL_RGB_INPUT: convert k5\n"); break;
		case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23:
		case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31:
		{
			*input_r = &zero_color.r;		*input_g = &zero_color.g;		*input_b = &zero_color.b;		break;
		}
	}
}

INLINE void SET_ADD_RGB_INPUT(UINT8 **input_r, UINT8 **input_g, UINT8 **input_b, int code)
{
	switch (code & 0x7)
	{
		case 0:		*input_r = &combined_color.r;	*input_g = &combined_color.g;	*input_b = &combined_color.b;	break;
		case 1:		*input_r = &texel0_color.r;		*input_g = &texel0_color.g;		*input_b = &texel0_color.b;		break;
		case 2:		*input_r = &texel1_color.r;		*input_g = &texel1_color.g;		*input_b = &texel1_color.b;		break;
		case 3:		*input_r = &prim_color.r;		*input_g = &prim_color.g;		*input_b = &prim_color.b;		break;
		case 4:		*input_r = &shade_color.r;		*input_g = &shade_color.g;		*input_b = &shade_color.b;		break;
		case 5:		*input_r = &env_color.r;		*input_g = &env_color.g;		*input_b = &env_color.b;		break;
		case 6:		*input_r = &one_color.r;		*input_g = &one_color.g;		*input_b = &one_color.b;		break;
		case 7:		*input_r = &zero_color.r;		*input_g = &zero_color.g;		*input_b = &zero_color.b;		break;
	}
}

INLINE void SET_SUB_ALPHA_INPUT(UINT8 **input, int code)
{
	switch (code & 0x7)
	{
		case 0:		*input = &combined_color.a; break;
		case 1:		*input = &texel0_color.a; break;
		case 2:		*input = &texel1_color.a; break;
		case 3:		*input = &prim_color.a; break;
		case 4:		*input = &shade_color.a; break;
		case 5:		*input = &env_color.a; break;
		case 6:		*input = &one_color.a; break;
		case 7:		*input = &zero_color.a; break;
	}
}

INLINE void SET_MUL_ALPHA_INPUT(UINT8 **input, int code)
{
	switch (code & 0x7)
	{
		case 0:		break;//TODO fatalerror("SET_MUL_ALPHA_INPUT: lod fraction\n"); break;
		case 1:		*input = &texel0_color.a; break;
		case 2:		*input = &texel1_color.a; break;
		case 3:		*input = &prim_color.a; break;
		case 4:		*input = &shade_color.a; break;
		case 5:		*input = &env_color.a; break;
		case 6:		break;//TODO fatalerror("SET_MUL_ALPHA_INPUT: primitive lod fraction\n"); break;
		case 7:		*input = &zero_color.a; break;
	}
}



INLINE COLOR COLOR_COMBINER(int cycle)
{
	COLOR c;
	UINT32 r, g, b, a;

	r = (((int)((UINT8)(*combiner_rgbsub_a_r[cycle]) - (UINT8)(*combiner_rgbsub_b_r[cycle])) *
		*combiner_rgbmul_r[cycle]) >> 8) + *combiner_rgbadd_r[cycle];

	g = (((int)((UINT8)(*combiner_rgbsub_a_g[cycle]) - (UINT8)(*combiner_rgbsub_b_g[cycle])) *
		*combiner_rgbmul_g[cycle]) >> 8) + *combiner_rgbadd_g[cycle];

	b = (((int)((UINT8)(*combiner_rgbsub_a_b[cycle]) - (UINT8)(*combiner_rgbsub_b_b[cycle])) *
		*combiner_rgbmul_b[cycle]) >> 8) + *combiner_rgbadd_b[cycle];

	a = (((int)((UINT8)(*combiner_alphasub_a[cycle]) - (UINT8)(*combiner_alphasub_b[cycle])) *
		*combiner_alphamul[cycle]) >> 8) + *combiner_alphaadd[cycle];

	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;
	if (a > 255) a = 255;

	c.r = r; c.g = g; c.b = b; c.a = a;
	return c;
}



INLINE void SET_BLENDER_INPUT(int cycle, int which, UINT8 **input_r, UINT8 **input_g, UINT8 **input_b, UINT8 **input_a, int a, int b)
{
	switch (a & 0x3)
	{
		case 0:
		{
			if (cycle == 0)
			{
				*input_r = &pixel_color.r;
				*input_g = &pixel_color.g;
				*input_b = &pixel_color.b;
			}
			else
			{
				*input_r = &blended_pixel_color.r;
				*input_g = &blended_pixel_color.g;
				*input_b = &blended_pixel_color.b;
			}
			break;
		}

		case 1:
		{
			//fatalerror("SET_BLENDER_INPUT: cycle %d, input A: memory color\n", cycle);
			// TODO
			*input_r = &memory_color.r;
			*input_g = &memory_color.g;
			*input_b = &memory_color.b;
			break;
		}

		case 2:
		{
			*input_r = &blend_color.r;		*input_g = &blend_color.g;		*input_b = &blend_color.b;
			break;
		}

		case 3:
		{
			*input_r = &fog_color.r;		*input_g = &fog_color.g;		*input_b = &fog_color.b;
			break;
		}
	}

	if (which == 0)
	{
		switch (b & 0x3)
		{
			case 0:		*input_a = &pixel_color.a; break;
			case 1:		*input_a = &one_color.a; break;
			case 2:		*input_a = &shade_color.a; break;
			case 3:		*input_a = &zero_color.a; break;
		}
	}
	else
	{
		switch (b & 0x3)
		{
			case 0:		*input_a = &inv_pixel_color.a; break;
			case 1:		*input_a = &memory_color.a; break;
			case 2:		*input_a = &one_color.a; break;
			case 3:		*input_a = &zero_color.a; break;
		}
	}
}

static const UINT8 dither_matrix_4x4[16] =
{
	 0,  8,  2, 10,
	12,  4, 14,  6,
	 3, 11,  1,  9,
	15,  7, 13,  5
};

#define DITHER_RB(val,dith)	((((val) << 1) - ((val) >> 4) + ((val) >> 7) + (dith)) >> 1)
#define DITHER_G(val,dith)	((((val) << 2) - ((val) >> 4) + ((val) >> 6) + (dith)) >> 2)

INLINE void BLENDER1_16(UINT16 *fb, COLOR c, int dith)
{
	int r, g, b;

	pixel_color.r = c.r;	inv_pixel_color.r = 0xff - c.r;
	pixel_color.g = c.g;	inv_pixel_color.g = 0xff - c.g;
	pixel_color.b = c.b;	inv_pixel_color.b = 0xff - c.b;
	pixel_color.a = c.a;	inv_pixel_color.a = 0xff - c.a;



	if (other_modes.image_read_en)
	{
		UINT16 mem = *fb;
		memory_color.r = ((mem >> 11) & 0x1f) << 3;
		memory_color.g = ((mem >>  6) & 0x1f) << 3;
		memory_color.b = ((mem >>  1) & 0x1f) << 3;
		memory_color.a = 0;
	}

	r = (((int)(*blender1a_r[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_r[0]) * (int)(*blender2b_a[0])) >> 8);

	g = (((int)(*blender1a_g[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_g[0]) * (int)(*blender2b_a[0])) >> 8);

	b = (((int)(*blender1a_b[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_b[0]) * (int)(*blender2b_a[0])) >> 8);

	//r = g = b = 0xff;

	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;

	if (other_modes.rgb_dither_sel != 3)
	{
		r = DITHER_RB(r, dith);
		g = DITHER_G (g, dith);
		b = DITHER_RB(b, dith);
	}

	if (c.a != 0)
	{
		*fb = ((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 1);
	}
}

INLINE void BLENDER2_16(UINT16 *fb, COLOR c1, COLOR c2, int dith)
{
	int r, g, b;

	pixel_color.r = c1.r;	inv_pixel_color.r = 0xff - c1.r;
	pixel_color.g = c1.g;	inv_pixel_color.g = 0xff - c1.g;
	pixel_color.b = c1.b;	inv_pixel_color.b = 0xff - c1.b;
	pixel_color.a = c1.a;	inv_pixel_color.a = 0xff - c1.a;

	if (other_modes.image_read_en)
	{
		UINT16 mem = *fb;
		memory_color.r = ((mem >> 11) & 0x1f) << 3;
		memory_color.g = ((mem >>  6) & 0x1f) << 3;
		memory_color.b = ((mem >>  1) & 0x1f) << 3;
		memory_color.a = 0;
	}

	r = (((int)(*blender1a_r[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_r[0]) * (int)(*blender2b_a[0])) >> 8);

	g = (((int)(*blender1a_g[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_g[0]) * (int)(*blender2b_a[0])) >> 8);

	b = (((int)(*blender1a_b[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_b[0]) * (int)(*blender2b_a[0])) >> 8);

	blended_pixel_color.r = r;
	blended_pixel_color.g = g;
	blended_pixel_color.b = b;

	pixel_color.r = c2.r;	inv_pixel_color.r = 0xff - c2.r;
	pixel_color.g = c2.g;	inv_pixel_color.g = 0xff - c2.g;
	pixel_color.b = c2.b;	inv_pixel_color.b = 0xff - c2.b;
	pixel_color.a = c2.a;	inv_pixel_color.a = 0xff - c2.a;

	r = (((int)(*blender1a_r[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_r[1]) * (int)(*blender2b_a[1])) >> 8);

	g = (((int)(*blender1a_g[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_g[1]) * (int)(*blender2b_a[1])) >> 8);

	b = (((int)(*blender1a_b[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_b[1]) * (int)(*blender2b_a[1])) >> 8);

  // VP ?????
	r = c1.r;
	g = c1.g;
	b = c1.b;

	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;

	if (other_modes.rgb_dither_sel != 3)
	{
		r = DITHER_RB(r, dith);
		g = DITHER_G (g, dith);
		b = DITHER_RB(b, dith);
	}

	{
		*fb = ((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 1);
	}
}

/*****************************************************************************/

static void fill_rectangle_16bit(RECTANGLE *rect)
{
	UINT16 *fb = (UINT16*)&rdram[(fb_address / 4)];
	int index, i, j;
	int x1 = rect->xh / 4;
	int x2 = rect->xl / 4;
	int y1 = rect->yh / 4;
	int y2 = rect->yl / 4;
	int clipx1, clipx2, clipy1, clipy2;

	UINT16 fill_colors[2] = {
    (fill_color >>  0) & 0xffff,
    (fill_color >> 16) & 0xffff,
  };

	// TODO: clip
	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	// clip
	if (x1 < clipx1)
	{
		x1 = clipx1;
	}
	if (y1 < clipy1)
	{
		y1 = clipy1;
	}
	if (x2 >= clipx2)
	{
		x2 = clipx2-1;
	}
	if (y2 >= clipy2)
	{
		y2 = clipy2-1;
	}

	if (other_modes.cycle_type == CYCLE_TYPE_FILL)
	{
    
		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				fb[(index + i) ^ 1] = fill_colors[i & 1];
			}
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_1)
	{
		shade_color.r = prim_color.r;
		shade_color.g = prim_color.g;
		shade_color.b = prim_color.b;
		shade_color.a = prim_color.a;

		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				COLOR c = COLOR_COMBINER(0);

				//BLENDER1_16(&fb[(index + i) ^ 1], c);
				{
					int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
					BLENDER1_16(&fb[(index + i) ^ WORD_ADDR_XOR], c, dith);
				}
			}
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_2)
	{
		shade_color.r = prim_color.r;
		shade_color.g = prim_color.g;
		shade_color.b = prim_color.b;
		shade_color.a = prim_color.a;

		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				COLOR c1 = COLOR_COMBINER(0);
				COLOR c2 = COLOR_COMBINER(1);

				//BLENDER1_16(&fb[(index + i) ^ 1], c);
				{
					int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
					BLENDER2_16(&fb[(index + i) ^ WORD_ADDR_XOR], c1, c2, dith);
				}
			}
		}
	}
	else
	{
		fatalerror("fill_rectangle_16bit: cycle type copy");
	}
}

#define XOR_SWAP_BYTE	4
#define XOR_SWAP_WORD	2
#define XOR_SWAP_DWORD	1

INLINE void FETCH_TEXEL(COLOR *color, int s, int t, UINT32 twidth, UINT32 tformat, UINT32 tsize, UINT32 tbase)
{
	if (t < 0) t = 0;
	if (s < 0) s = 0;

	switch (tformat)
	{
		case 0:		// RGBA
		{
			switch (tsize)
			{
				case 0: break;
				case 1: break;	// FIXME?: Extreme-G 2 does this
				case PIXEL_SIZE_16BIT:
				{
					UINT16 *tc = (UINT16*)texture_cache;
					int taddr = ((tbase/2) + ((t) * (twidth/2)) + (s))  ^ ((t & 1) ? XOR_SWAP_WORD : 0);
					UINT16 c = tc[taddr ^ WORD_ADDR_XOR];

					color->r = ((c >> 11) & 0x1f) << 3;
					color->g = ((c >>  6) & 0x1f) << 3;
					color->b = ((c >>  1) & 0x1f) << 3;
					color->a = (c & 1) ? 0xff : 0;
					break;
				}
				case PIXEL_SIZE_32BIT:
				{
					UINT32 *tc = (UINT32*)texture_cache;
					int taddr = ((tbase/4) + ((t) * (twidth/2)) + (s)) ^ ((t & 1) ? XOR_SWAP_DWORD : 0);
					UINT32 c = tc[taddr];

					color->r = ((c >> 24) & 0xff);
					color->g = ((c >> 16) & 0xff);
					color->b = ((c >>  8) & 0xff);
					color->a = ((c >>  0) & 0xff);
					break;
				}
				default:		fatalerror("FETCH_TEXEL: unknown RGBA texture size %d\n", tsize); break;
			}
			break;
		}
		case 2:		// Color Index
		{
			switch (tsize)
			{
				case PIXEL_SIZE_4BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s) / 2)) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 p = ((s) & 1) ? (tc[taddr ^ BYTE_ADDR_XOR] & 0xf) : (tc[taddr ^ BYTE_ADDR_XOR] >> 4);
					UINT16 c = tlut[p ^ WORD_ADDR_XOR];

					if (other_modes.tlut_type == 0)
					{
						color->r = ((c >> 11) & 0x1f) << 3;
						color->g = ((c >>  6) & 0x1f) << 3;
						color->b = ((c >>  1) & 0x1f) << 3;
						color->a = (c & 1) ? 0xff : 0;
					}
					else
					{
						color->r = color->g = color->b = (c >> 8) & 0xff;
						color->a = c & 0xff;
					}
					break;
				}
				case PIXEL_SIZE_8BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s))) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 p = tc[taddr ^ BYTE_ADDR_XOR];
					UINT16 c = tlut[p ^ WORD_ADDR_XOR];

					if (other_modes.tlut_type == 0)
					{
						color->r = ((c >> 11) & 0x1f) << 3;
						color->g = ((c >>  6) & 0x1f) << 3;
						color->b = ((c >>  1) & 0x1f) << 3;
						color->a = (c & 1) ? 0xff : 0;
					}
					else
					{
						color->r = color->g = color->b = (c >> 8) & 0xff;
						color->a = c & 0xff;
					}
					break;
				}
				case 2: break;		// FIXME?: Clay Fighter Sculptor's Cut does this..
				default:		fatalerror("FETCH_TEXEL: unknown CI texture size %d\n", tsize); break;
			}
			break;
		}
		case 3:		// Intensity + Alpha
		{
			switch (tsize)
			{
				case PIXEL_SIZE_4BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s) / 2)) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 p = ((s) & 1) ? (tc[taddr ^ BYTE_ADDR_XOR] & 0xf) : (tc[taddr ^ BYTE_ADDR_XOR] >> 4);
					UINT8 i = ((p & 0xe) << 4) | ((p & 0xe) << 1) | (p & 0xe >> 2);

					color->r = i;
					color->g = i;
					color->b = i;
					color->a = (p & 0x1) ? 0xff : 0;
					break;
				}
				case PIXEL_SIZE_8BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s))) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 p = tc[taddr ^ BYTE_ADDR_XOR];
					UINT8 i = (p >> 4) | (p & 0xf0);

					color->r = i;
					color->g = i;
					color->b = i;
					color->a = (p & 0xf) | ((p << 4) & 0xf0);
					break;
				}
				case PIXEL_SIZE_16BIT:
				{
					UINT16 *tc = (UINT16*)texture_cache;
					int taddr = (tbase + ((t) * (twidth/2)) + (s)) ^ ((t & 1) ? XOR_SWAP_WORD : 0);
					UINT16 c = tc[taddr ^ WORD_ADDR_XOR];
					UINT8 i = (c >> 8);

					color->r = i;
					color->g = i;
					color->b = i;
					color->a = c & 0xff;
					break;
				}
				default:		fatalerror("FETCH_TEXEL: unknown IA texture size %d\n", tsize); break;
			}
			break;
		}
		case 4:		// Intensity
		{
			switch (tsize)
			{
				case PIXEL_SIZE_4BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s) / 2)) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 c = ((s) & 1) ? (tc[taddr ^ BYTE_ADDR_XOR] & 0xf) : (tc[taddr ^ BYTE_ADDR_XOR] >> 4);
					c |= (c << 4);

					color->r = c;
					color->g = c;
					color->b = c;
					color->a = c;
					break;
				}
				case PIXEL_SIZE_8BIT:
				{
					UINT8 *tc = (UINT8*)texture_cache;
					int taddr = (tbase + ((t) * twidth) + ((s))) ^ ((t & 1) ? XOR_SWAP_BYTE : 0);
					UINT8 c = tc[taddr ^ BYTE_ADDR_XOR];

					color->r = c;
					color->g = c;
					color->b = c;
					color->a = c;
					break;
				}
				default:		fatalerror("FETCH_TEXEL: unknown I texture size %d\n", tsize); break;
			}
			break;
		}
		default:
		{
			fatalerror("FETCH_TEXEL: unknown texture format %d\n", tformat);
			break;
		}
	}
}

#if 1
#define TEXTURE_PIPELINE(TEX, SSS, SST, NOBILINEAR)																					\
do																																	\
{																																	\
	if (other_modes.sample_type && !NOBILINEAR)																						\
	{																																\
		COLOR t0, t1, t2, t3;																										\
		int sss1, sst1, sss2, sst2;																									\
																																	\
		/* bilinear */																												\
																																	\
		sss1 = (SSS >> 10) - (tsl >> 2);																							\
		sss2 = sss1 + 1;																											\
																																	\
		sst1 = (SST >> 10) - (ttl >> 2);																							\
		sst2 = sst1 + 1;																											\
																																	\
		CLAMP(sss1, sst1);																											\
		CLAMP(sss2, sst2);																											\
																																	\
		FETCH_TEXEL(&t0, sss1, sst1, twidth, tformat, tsize, tbase);																\
		FETCH_TEXEL(&t1, sss2, sst1, twidth, tformat, tsize, tbase);																\
		FETCH_TEXEL(&t2, sss1, sst2, twidth, tformat, tsize, tbase);																\
		FETCH_TEXEL(&t3, sss2, sst2, twidth, tformat, tsize, tbase);																\
																																	\
		TEX.r = (((( (t0.r * (0x3ff - (SSS & 0x3ff))) + (t1.r * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
						 (((( (t2.r * (0x3ff - (SSS & 0x3ff))) + (t3.r * ((SSS & 0x3ff))) ) >> 10) * ((SST & 0x3ff))) >> 10);		\
								 																									\
		TEX.g = (((( (t0.g * (0x3ff - (SSS & 0x3ff))) + (t1.g * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
						 (((( (t2.g * (0x3ff - (SSS & 0x3ff))) + (t3.g * ((SSS & 0x3ff))) ) >> 10) * ((SST & 0x3ff))) >> 10);		\
								 																									\
		TEX.b = (((( (t0.b * (0x3ff - (SSS & 0x3ff))) + (t1.b * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
						 (((( (t2.b * (0x3ff - (SSS & 0x3ff))) + (t3.b * ((SSS & 0x3ff))) ) >> 10) * ((SST & 0x3ff))) >> 10);		\
								 																									\
		TEX.a = (((( (t0.a * (0x3ff - (SSS & 0x3ff))) + (t1.a * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
						 (((( (t2.a * (0x3ff - (SSS & 0x3ff))) + (t3.a * ((SSS & 0x3ff))) ) >> 10) * ((SST & 0x3ff))) >> 10);		\
	}																																\
	else																															\
	{																																\
		int sss1, sst1;																												\
		sss1 = (SSS >> 10) - (tsl >> 2);																							\
		if ((SSS & 0x3ff) >= 0x200) sss1++;																							\
																																	\
		sst1 = (SST >> 10) - (ttl >> 2);																							\
		if ((SST & 0x3ff) >= 0x200) sst1++;																							\
																																	\
		CLAMP(sss1, sst1);																											\
																																	\
		/* point sample */																											\
		FETCH_TEXEL(&TEX, sss1, sst1, twidth, tformat, tsize, tbase);																\
	}																																\
}																																	\
while(0)
#endif

// This is the implementation with 3-sample bilinear filtering, which should be more correct, but not yet working properly
// TODO: check the correct texel samples and weighting values

#if 1
#define TEXTURE_PIPELINE(TEX, SSS, SST, NOBILINEAR)																					\
do																																	\
{																																	\
	if (other_modes.sample_type && !NOBILINEAR)																						\
	{																																\
		COLOR t0, t1, t2;																											\
		int sss1, sst1, sss2, sst2;																									\
																																	\
		/* bilinear */																												\
																																	\
		sss1 = (SSS >> 10) - (tsl >> 2);																							\
		sss2 = sss1 + 1;																											\
																																	\
		sst1 = (SST >> 10) - (ttl >> 2);																							\
		sst2 = sst1 + 1;																											\
																																	\
		CLAMP(sss1, sst1);																											\
		CLAMP(sss2, sst2);																											\
																																	\
		FETCH_TEXEL(&t0, sss1, sst1, twidth, tformat, tsize, tbase);																\
		FETCH_TEXEL(&t1, sss2, sst1, twidth, tformat, tsize, tbase);																\
		FETCH_TEXEL(&t2, sss2, sst2, twidth, tformat, tsize, tbase);																\
																																	\
		TEX.r = (((( (t0.r * (0x3ff - (SSS & 0x3ff))) + (t1.r * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
				(( (t2.r) * ((SST & 0x3ff))) >> 10);																				\
								 																									\
		TEX.g = (((( (t0.g * (0x3ff - (SSS & 0x3ff))) + (t1.g * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
				(( (t2.g) * ((SST & 0x3ff))) >> 10);																				\
							 																										\
		TEX.b = (((( (t0.b * (0x3ff - (SSS & 0x3ff))) + (t1.b * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
				(( (t2.b) * ((SST & 0x3ff))) >> 10);																				\
								 																									\
		TEX.a = (((( (t0.a * (0x3ff - (SSS & 0x3ff))) + (t1.a * ((SSS & 0x3ff))) ) >> 10) * (0x3ff - (SST & 0x3ff))) >> 10) +		\
				(( (t2.a) * ((SST & 0x3ff))) >> 10);																				\
	}																																\
	else																															\
	{																																\
		int sss1, sst1;																												\
		sss1 = (SSS >> 10) - (tsl >> 2);																							\
		if ((SSS & 0x3ff) >= 0x200) sss1++;																							\
																																	\
		sst1 = (SST >> 10) - (ttl >> 2);																							\
		if ((SST & 0x3ff) >= 0x200) sst1++;																							\
																																	\
		CLAMP(sss1, sst1);																											\
																																	\
		/* point sample */																											\
		FETCH_TEXEL(&TEX, sss1, sst1, twidth, tformat, tsize, tbase);																\
	}																																\
}																																	\
while(0)
#endif

#define TEXTURE_PIPELINE1(SSS, SST, NOBILINEAR)				\
do															\
{															\
	TEXTURE_PIPELINE(texel0_color, SSS, SST, NOBILINEAR);	\
}															\
while(0)

#define TEXTURE_PIPELINE2(SSS, SST, NOBILINEAR)				\
do															\
{															\
	TEXTURE_PIPELINE(texel0_color, SSS, SST, NOBILINEAR);	\
	TEXTURE_PIPELINE(texel1_color, SSS, SST, NOBILINEAR);	\
}															\
while(0)



static void texture_rectangle_16bit(TEX_RECTANGLE *rect)
{
	UINT16 *fb = (UINT16*)&rdram[(fb_address / 4)];
	int i, j;
	int x1, x2, y1, y2;
	int tsl, tsh, ttl, tth;
	int s, t;
	int twidth, tformat, tsize, tbase;
	int clamp_s, clamp_t, mask_s, mask_t, mirror_s, mirror_t;
	int clipx1, clipx2, clipy1, clipy2;

	x1 = (rect->xh / 4);
	x2 = (rect->xl / 4);
	y1 = (rect->yh / 4);
	y2 = (rect->yl / 4);

	if (other_modes.cycle_type == CYCLE_TYPE_FILL || other_modes.cycle_type == CYCLE_TYPE_COPY)
	{
		rect->dsdx /= 4;
		x2 += 1;
		y2 += 1;
	}

	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	// clip
	if (x1 < clipx1)
	{
		x1 = clipx1;
	}
	if (y1 < clipy1)
	{
		y1 = clipy1;
	}
	if (x2 >= clipx2)
	{
		x2 = clipx2-1;
	}
	if (y2 >= clipy2)
	{
		y2 = clipy2-1;
	}

	tsl = tile[rect->tilenum].sl;
	tsh = tile[rect->tilenum].sh;
	ttl = tile[rect->tilenum].tl;
	tth = tile[rect->tilenum].th;

	twidth = tile[rect->tilenum].line;
	tformat = tile[rect->tilenum].format;
	tsize = tile[rect->tilenum].size;
	tbase = tile[rect->tilenum].tmem;

	// FIXME?: clamping breaks at least Rampage World Tour
	clamp_t = 0; //tile[rect->tilenum].ct;
	clamp_s = 0; //tile[rect->tilenum].cs;

	mirror_t = tile[rect->tilenum].mt;
	mirror_s = tile[rect->tilenum].ms;
	mask_t = tile[rect->tilenum].mask_t;
	mask_s = tile[rect->tilenum].mask_s;

	t = (int)(rect->t) << 5;

	if (other_modes.cycle_type == CYCLE_TYPE_1 || other_modes.cycle_type == CYCLE_TYPE_2)
	{
	}

	if (other_modes.cycle_type == CYCLE_TYPE_1)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
				COLOR c;

				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				//CLAMP(ss, st);
				//FETCH_TEXEL(&texel0_color, ss, st, twidth, tformat, tsize, tbase);

				TEXTURE_PIPELINE1(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				c = COLOR_COMBINER(0);

				{
					int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
					BLENDER1_16(&fb[(fb_index + i) ^ WORD_ADDR_XOR], c, dith);
				}

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_2)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
				COLOR c1, c2;

				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				//CLAMP(ss, st);
				//FETCH_TEXEL(&texel0_color, ss, st, twidth, tformat, tsize, tbase);
				//FETCH_TEXEL(&texel1_color, ss, st, twidth, tformat, tsize, tbase);

				TEXTURE_PIPELINE2(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				c1 = COLOR_COMBINER(0);
				c2 = COLOR_COMBINER(1);

				//BLENDER2_16(&fb[(fb_index + i) ^ WORD_ADDR_XOR], c1, c2);
				{
					int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
					BLENDER2_16(&fb[(fb_index + i) ^ WORD_ADDR_XOR], c1, c2, dith);
				}

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_COPY)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
			/*
                if (clamp_s)
                {
                    if (s < (tsl << 8)) s = (tsl << 8);
                    if (s > (tsh << 8)) s = (tsh << 8);
                }
                if (clamp_t)
                {
                    if (t < (ttl << 8)) t = (ttl << 8);
                    if (t > (tth << 8)) t = (tth << 8);
                }
                if (mask_s)
                {
                    s &= (((UINT32)(0xffffffff) >> (32-mask_s)) << 10) | 0x3ff;
                }
                if (mask_t)
                {
                    t &= (((UINT32)(0xffffffff) >> (32-mask_t)) << 10) | 0x3ff;
                }

                FETCH_TEXEL(&c, (s-tile[rect->tilenum].sl) >> 10, (t-tile[rect->tilenum].tl) >> 10, twidth, tformat, tsize, tbase);
            */
				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				//CLAMP(ss, st);
				//FETCH_TEXEL(&c, ss, st, twidth, tformat, tsize, tbase);

				TEXTURE_PIPELINE1(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				if (texel0_color.a != 0)
				{
					fb[(fb_index + i) ^ WORD_ADDR_XOR] = ((texel0_color.r >> 3) << 11) | ((texel0_color.g >> 3) << 6) | ((texel0_color.b >> 3) << 1);
				}

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else
	{
		fatalerror("texture_rectangle_16bit: unknown cycle type %d\n", other_modes.cycle_type);
	}
}

/*****************************************************************************/

INLINE void BLENDER1_32(UINT32 *fb, COLOR c)
{
	int r, g, b;

	pixel_color.r = c.r;	inv_pixel_color.r = 0xff - c.r;
	pixel_color.g = c.g;	inv_pixel_color.g = 0xff - c.g;
	pixel_color.b = c.b;	inv_pixel_color.b = 0xff - c.b;
	pixel_color.a = c.a;	inv_pixel_color.a = 0xff - c.a;

	if (other_modes.image_read_en)
	{
		UINT32 mem = *fb;
		memory_color.r = ((mem >> 16) & 0xff);
		memory_color.g = ((mem >>  8) & 0xff);
		memory_color.b = ((mem >>  0) & 0xff);
		memory_color.a = 0;
	}

	r = (((int)(*blender1a_r[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_r[0]) * (int)(*blender2b_a[0])) >> 8);

	g = (((int)(*blender1a_g[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_g[0]) * (int)(*blender2b_a[0])) >> 8);

	b = (((int)(*blender1a_b[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_b[0]) * (int)(*blender2b_a[0])) >> 8);

	//r = g = b = 0xff;

	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;

	*fb = (r << 16) | (g << 8) | b;
}

INLINE void BLENDER2_32(UINT32 *fb, COLOR c1, COLOR c2)
{
	int r, g, b;

	pixel_color.r = c1.r;	inv_pixel_color.r = 0xff - c1.r;
	pixel_color.g = c1.g;	inv_pixel_color.g = 0xff - c1.g;
	pixel_color.b = c1.b;	inv_pixel_color.b = 0xff - c1.b;
	pixel_color.a = c1.a;	inv_pixel_color.a = 0xff - c1.a;

	if (other_modes.image_read_en)
	{
		UINT32 mem = *fb;
		memory_color.r = ((mem >> 16) & 0xff);
		memory_color.g = ((mem >>  8) & 0xff);
		memory_color.b = ((mem >>  0) & 0xff);
		memory_color.a = 0;
	}

	r = (((int)(*blender1a_r[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_r[0]) * (int)(*blender2b_a[0])) >> 8);

	g = (((int)(*blender1a_g[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_g[0]) * (int)(*blender2b_a[0])) >> 8);

	b = (((int)(*blender1a_b[0]) * (int)(*blender1b_a[0])) >> 8) +
		(((int)(*blender2a_b[0]) * (int)(*blender2b_a[0])) >> 8);

	blended_pixel_color.r = r;
	blended_pixel_color.g = g;
	blended_pixel_color.b = b;

	pixel_color.r = c2.r;	inv_pixel_color.r = 0xff - c2.r;
	pixel_color.g = c2.g;	inv_pixel_color.g = 0xff - c2.g;
	pixel_color.b = c2.b;	inv_pixel_color.b = 0xff - c2.b;
	pixel_color.a = c2.a;	inv_pixel_color.a = 0xff - c2.a;

	r += (((int)(*blender1a_r[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_r[1]) * (int)(*blender2b_a[1])) >> 8);

	g += (((int)(*blender1a_g[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_g[1]) * (int)(*blender2b_a[1])) >> 8);

	b += (((int)(*blender1a_b[1]) * (int)(*blender1b_a[1])) >> 8) +
		(((int)(*blender2a_b[1]) * (int)(*blender2b_a[1])) >> 8);

	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;

  // VP ??????
	*fb = (c1.r << 16) | (c1.g << 8) | c1.b;
	//*fb = (r << 16) | (g << 8) | b;
}

static void fill_rectangle_32bit(RECTANGLE *rect)
{
	UINT32 *fb = (UINT32*)&rdram[(fb_address / 4)];
	int index, i, j;
	int x1 = rect->xh / 4;
	int x2 = rect->xl / 4;
	int y1 = rect->yh / 4;
	int y2 = rect->yl / 4;
	int clipx1, clipx2, clipy1, clipy2;

	// TODO: clip
	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	// clip
	if (x1 < clipx1)
	{
		x1 = clipx1;
	}
	if (y1 < clipy1)
	{
		y1 = clipy1;
	}
	if (x2 >= clipx2)
	{
		x2 = clipx2-1;
	}
	if (y2 >= clipy2)
	{
		y2 = clipy2-1;
	}

	if (other_modes.cycle_type == CYCLE_TYPE_FILL)
	{
		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				fb[(index + i) ^ 1] = fill_color;
			}
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_1)
	{
		shade_color.r = prim_color.r;
		shade_color.g = prim_color.g;
		shade_color.b = prim_color.b;
		shade_color.a = prim_color.a;

		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				COLOR c = COLOR_COMBINER(0);

				BLENDER1_32(&fb[(index + i)], c);
			}
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_2)
	{
		shade_color.r = prim_color.r;
		shade_color.g = prim_color.g;
		shade_color.b = prim_color.b;
		shade_color.a = prim_color.a;

		for (j=y1; j <= y2; j++)
		{
			index = j * fb_width;
			for (i=x1; i <= x2; i++)
			{
				COLOR c1 = COLOR_COMBINER(0);
				COLOR c2 = COLOR_COMBINER(1);

				BLENDER2_32(&fb[(index + i)], c1, c2);
			}
		}
	}
	else
	{
		fatalerror("fill_rectangle_32bit: cycle type copy");
	}
}

static void texture_rectangle_32bit(TEX_RECTANGLE *rect)
{
	UINT32 *fb = (UINT32*)&rdram[(fb_address / 4)];
	int i, j;
	int x1, x2, y1, y2;
	int tsl, tsh, ttl, tth;
	int s, t;
	int twidth, tformat, tsize, tbase;
	int clamp_s, clamp_t, mask_s, mask_t, mirror_s, mirror_t;
	int clipx1, clipx2, clipy1, clipy2;

	x1 = (rect->xh / 4);
	x2 = (rect->xl / 4);
	y1 = (rect->yh / 4);
	y2 = (rect->yl / 4);

	if (other_modes.cycle_type == CYCLE_TYPE_FILL || other_modes.cycle_type == CYCLE_TYPE_COPY)
	{
		rect->dsdx /= 4;
		x2 += 1;
		y2 += 1;
	}

	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	// clip
	if (x1 < clipx1)
	{
		x1 = clipx1;
	}
	if (y1 < clipy1)
	{
		y1 = clipy1;
	}
	if (x2 >= clipx2)
	{
		x2 = clipx2-1;
	}
	if (y2 >= clipy2)
	{
		y2 = clipy2-1;
	}

	tsl = tile[rect->tilenum].sl;
	tsh = tile[rect->tilenum].sh;
	ttl = tile[rect->tilenum].tl;
	tth = tile[rect->tilenum].th;

	twidth = tile[rect->tilenum].line;
	tformat = tile[rect->tilenum].format;
	tsize = tile[rect->tilenum].size;
	tbase = tile[rect->tilenum].tmem;

	// FIXME?: clamping breaks at least Rampage World Tour
	clamp_t = 0; //tile[rect->tilenum].ct;
	clamp_s = 0; //tile[rect->tilenum].cs;

	mirror_t = tile[rect->tilenum].mt;
	mirror_s = tile[rect->tilenum].ms;
	mask_t = tile[rect->tilenum].mask_t;
	mask_s = tile[rect->tilenum].mask_s;

	t = (int)(rect->t) << 5;

	if (other_modes.cycle_type == CYCLE_TYPE_1 || other_modes.cycle_type == CYCLE_TYPE_2)
	{
	}

	if (other_modes.cycle_type == CYCLE_TYPE_1)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
				COLOR c;

				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				//CLAMP(ss, st);
				//FETCH_TEXEL(&texel0_color, ss, st, twidth, tformat, tsize, tbase);

				TEXTURE_PIPELINE1(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				c = COLOR_COMBINER(0);

				BLENDER1_32(&fb[(fb_index + i)], c);

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_2)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
				COLOR c1, c2;

				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				//CLAMP(ss, st);
				//FETCH_TEXEL(&texel0_color, ss, st, twidth, tformat, tsize, tbase);
				//FETCH_TEXEL(&texel1_color, ss, st, twidth, tformat, tsize, tbase);

				TEXTURE_PIPELINE2(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				c1 = COLOR_COMBINER(0);
				c2 = COLOR_COMBINER(1);

				BLENDER2_32(&fb[(fb_index + i)], c1, c2);

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else if (other_modes.cycle_type == CYCLE_TYPE_COPY)
	{
		for (j = y1; j < y2; j++)
		{
			int fb_index = j * fb_width;

			s = (int)(rect->s) << 5;

			for (i = x1; i < x2; i++)
			{
			/*
                if (clamp_s)
                {
                    if (s < (tsl << 8)) s = (tsl << 8);
                    if (s > (tsh << 8)) s = (tsh << 8);
                }
                if (clamp_t)
                {
                    if (t < (ttl << 8)) t = (ttl << 8);
                    if (t > (tth << 8)) t = (tth << 8);
                }
                if (mask_s)
                {
                    s &= (((UINT32)(0xffffffff) >> (32-mask_s)) << 10) | 0x3ff;
                }
                if (mask_t)
                {
                    t &= (((UINT32)(0xffffffff) >> (32-mask_t)) << 10) | 0x3ff;
                }

                FETCH_TEXEL(&c, (s-tile[rect->tilenum].sl) >> 10, (t-tile[rect->tilenum].tl) >> 10, twidth, tformat, tsize, tbase);
            */
				//ss = (s >> 10) - (tsl >> 2);
				//if ((s & 0x3ff) >= 0x200) ss++;
				//st = (t >> 10) - (ttl >> 2);
				//if ((t & 0x3ff) >= 0x200) st++;

				TEXTURE_PIPELINE1(s, t, (rect->dsdx == (1 << 10)) && (rect->dtdy == (1 << 10)));

				//CLAMP(ss, st);
				//FETCH_TEXEL(&c, ss, st, twidth, tformat, tsize, tbase);

				if (texel0_color.a != 0)
				{
					fb[(fb_index + i) ^ WORD_ADDR_XOR] = (texel0_color.r << 16) | (texel0_color.g << 8) | (texel0_color.b << 0);
				}

				s += rect->dsdx;
			}

			t += rect->dtdy;
		}
	}
	else
	{
		fatalerror("texture_rectangle_32bit: unknown cycle type %d\n", other_modes.cycle_type);
	}
}


static void render_spans_32(int start, int end, int tilenum, int shade, int texture, int zbuffer, int flip)
{
	UINT32 *fb = (UINT32*)&rdram[fb_address / 4];
	UINT16 *zb = (UINT16*)&rdram[zb_address / 4];
	int i, j;
	int tsl, tsh, ttl, tth;
	int twidth, tformat, tsize, tbase;
	int clamp_s, clamp_t, mask_s, mask_t, mirror_s, mirror_t;

	int clipx1, clipx2, clipy1, clipy2;

	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	tsl = tile[tilenum].sl;
	tsh = tile[tilenum].sh;
	ttl = tile[tilenum].tl;
	tth = tile[tilenum].th;

	twidth = tile[tilenum].line;
	tformat = tile[tilenum].format;
	tsize = tile[tilenum].size;
	tbase = tile[tilenum].tmem;

	clamp_t = tile[tilenum].ct;
	clamp_s = tile[tilenum].cs;
	mirror_t = tile[tilenum].mt;
	mirror_s = tile[tilenum].ms;
	mask_t = tile[tilenum].mask_t;
	mask_s = tile[tilenum].mask_s;

	if (start < clipy1)
	{
		start = clipy1;
	}
	if (start >= clipy2)
	{
		start = clipy2-1;
	}
	if (end < clipy1)
	{
		end = clipy1;
	}
	if (end >= clipy2)
	{
		end = clipy2-1;
	}

	for (i=start; i <= end; i++)
	{
		int xstart = span[i].lx;
		int xend = span[i].rx;
		int r = span[i].r;
		int g = span[i].g;
		int b = span[i].b;
		int a = span[i].a;
		int z = span[i].z;
		int s = span[i].s;
		int t = span[i].t;
		int w = span[i].w;
		int dr = span[i].dr;
		int dg = span[i].dg;
		int db = span[i].db;
		int da = span[i].da;
		int dz = span[i].dz;
		int ds = span[i].ds;
		int dt = span[i].dt;
		int dw = span[i].dw;
		int drinc, dginc, dbinc, dainc, dzinc, dsinc, dtinc, dwinc;

		int x;
		int xinc;

		int fb_index = fb_width * i;
		int length;

		drinc = flip ? (dr) : -dr;
		dginc = flip ? (dg) : -dg;
		dbinc = flip ? (db) : -db;
		dainc = flip ? (da) : -da;
		dzinc = flip ? (dz) : -dz;
		dsinc = flip ? (ds) : -ds;
		dtinc = flip ? (dt) : -dt;
		dwinc = flip ? (dw) : -dw;

		xinc = flip ? 1 : -1;
		x = xend;

		length = flip ? (xstart - xend) : (xend - xstart);

		for (j=0; j <= length; j++)
		{
			int sr = r >> 16;
			int sg = g >> 16;
			int sb = b >> 16;
			int sa = a >> 16;
			int ss = s >> 16;
			int st = t >> 16;
			int sw = w >> 16;
			UINT16 sz = z >> 16;
			int oz;
			int sss = 0, sst = 0;

			if (x >= clipx1 && x < clipx2)
			{
				COLOR c1, c2;
				c1.r = c1.g = c1.b = c1.a = 0;
				c2.r = c2.g = c2.b = c2.a = 0;

				if ((z & 0xffff) >= 0x8000) sz++;
				if ((w & 0xffff) >= 0x8000) sw++;

				if (other_modes.persp_tex_en)
				{
					if (sw != 0)
					{
						sss = (((INT64)(ss) << 20) / sw);
						sst = (((INT64)(st) << 20) / sw);
					}
				}
				else
				{
					sss = ss;
					sst = st;
				}

				if (sr > 0xff) sr = 0xff;	if (sg > 0xff) sg = 0xff;	if (sb > 0xff) sb = 0xff;

				shade_color.r = sr; shade_color.g = sg; shade_color.b = sb; shade_color.a = sa;

				if (texture)
				{
					/*if (other_modes.sample_type)
                    {
                        // bilinear

                        sss1 = (sss >> 10) - (tsl >> 2);
                        sss2 = sss1 + 1;

                        sst1 = (sst >> 10) - (ttl >> 2);
                        sst2 = sst1 + 1;

                        CLAMP(sss1, sst1);
                        CLAMP(sss2, sst2);

                        FETCH_TEXEL(&t0, sss1, sst1, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t1, sss2, sst1, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t2, sss1, sst2, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t3, sss2, sst2, twidth, tformat, tsize, tbase);

                        texel0_color.r = (((( (t0.r * (0x3ff - (sss & 0x3ff))) + (t1.r * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.r * (0x3ff - (sss & 0x3ff))) + (t3.r * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.g = (((( (t0.g * (0x3ff - (sss & 0x3ff))) + (t1.g * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.g * (0x3ff - (sss & 0x3ff))) + (t3.g * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.b = (((( (t0.b * (0x3ff - (sss & 0x3ff))) + (t1.b * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.b * (0x3ff - (sss & 0x3ff))) + (t3.b * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.a = (((( (t0.a * (0x3ff - (sss & 0x3ff))) + (t1.a * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.a * (0x3ff - (sss & 0x3ff))) + (t3.a * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);
                    }
                    else
                    {
                        sss1 = (sss >> 10) - (tsl >> 2);
                        if ((sss & 0x3ff) >= 0x200) sss1++;

                        sst1 = (sst >> 10) - (ttl >> 2);
                        if ((sst & 0x3ff) >= 0x200) sst1++;

                        CLAMP(sss1, sst1);

                        // point sample
                        FETCH_TEXEL(&texel0_color, sss1, sst1, twidth, tformat, tsize, tbase);
                    }*/

					if (other_modes.cycle_type == CYCLE_TYPE_1)
					{
						TEXTURE_PIPELINE1(sss, sst, 0);
					}
					else
					{
						TEXTURE_PIPELINE2(sss, sst, 0);
					}
				}

				if (other_modes.cycle_type == CYCLE_TYPE_1)
				{
					c1 = COLOR_COMBINER(0);
				}
				else if (other_modes.cycle_type == CYCLE_TYPE_2)
				{
					c1 = COLOR_COMBINER(0);
					c2 = COLOR_COMBINER(1);
				}

				oz = (UINT16)zb[(fb_index + x) ^ WORD_ADDR_XOR];
				if (zbuffer)
				{
					if (sz < oz /*&& c.a != 0*/)
					{
						if (other_modes.cycle_type == CYCLE_TYPE_1)
						{
							BLENDER1_32(&fb[(fb_index + x)], c1);
						}
						else
						{
							BLENDER2_32(&fb[(fb_index + x)], c1, c2);
						}

						if (other_modes.z_compare_en && other_modes.z_update_en)
						{
							zb[(fb_index + x) ^ WORD_ADDR_XOR] = sz;
						}
					}
				}
				else
				{
					if (other_modes.cycle_type == CYCLE_TYPE_1)
					{
						BLENDER1_32(&fb[(fb_index + x)], c1);
					}
					else
					{
						BLENDER2_32(&fb[(fb_index + x)], c1, c2);
					}
				}
			}

			r += drinc;
			g += dginc;
			b += dbinc;
			a += dainc;
			z += dzinc;
			s += dsinc;
			t += dtinc;
			w += dwinc;

			x += xinc;
		}
	}
}

/*****************************************************************************/



static void render_spans_16(int start, int end, int tilenum, int shade, int texture, int zbuffer, int flip)
{
	UINT16 *fb = (UINT16*)&rdram[fb_address / 4];
	UINT16 *zb = (UINT16*)&rdram[zb_address / 4];
	int i, j;
	int tsl, tsh, ttl, tth;
	int twidth, tformat, tsize, tbase;
	int clamp_s, clamp_t, mask_s, mask_t, mirror_s, mirror_t;

	int clipx1, clipx2, clipy1, clipy2;

	clipx1 = clip.xh / 4;
	clipx2 = clip.xl / 4;
	clipy1 = clip.yh / 4;
	clipy2 = clip.yl / 4;

	tsl = tile[tilenum].sl;
	tsh = tile[tilenum].sh;
	ttl = tile[tilenum].tl;
	tth = tile[tilenum].th;

	twidth = tile[tilenum].line;
	tformat = tile[tilenum].format;
	tsize = tile[tilenum].size;
	tbase = tile[tilenum].tmem;

	clamp_t = tile[tilenum].ct;
	clamp_s = tile[tilenum].cs;
	mirror_t = tile[tilenum].mt;
	mirror_s = tile[tilenum].ms;
	mask_t = tile[tilenum].mask_t;
	mask_s = tile[tilenum].mask_s;

	if (start < clipy1)
	{
		start = clipy1;
	}
	if (start >= clipy2)
	{
		start = clipy2-1;
	}
	if (end < clipy1)
	{
		end = clipy1;
	}
	if (end >= clipy2)
	{
		end = clipy2-1;
	}

	for (i=start; i <= end; i++)
	{
		int xstart = span[i].lx;
		int xend = span[i].rx;
		int r = span[i].r;
		int g = span[i].g;
		int b = span[i].b;
		int a = span[i].a;
		int z = span[i].z;
		int s = span[i].s;
		int t = span[i].t;
		int w = span[i].w;
		int dr = span[i].dr;
		int dg = span[i].dg;
		int db = span[i].db;
		int da = span[i].da;
		int dz = span[i].dz;
		int ds = span[i].ds;
		int dt = span[i].dt;
		int dw = span[i].dw;
		int drinc, dginc, dbinc, dainc, dzinc, dsinc, dtinc, dwinc;

		int x;
		int xinc;

		int fb_index = fb_width * i;
		int length;

		drinc = flip ? (dr) : -dr;
		dginc = flip ? (dg) : -dg;
		dbinc = flip ? (db) : -db;
		dainc = flip ? (da) : -da;
		dzinc = flip ? (dz) : -dz;
		dsinc = flip ? (ds) : -ds;
		dtinc = flip ? (dt) : -dt;
		dwinc = flip ? (dw) : -dw;

		/*if (flip)
        {
            if (xend > clipx2)
            {
                int d = xend-clipx2;
                r += d * drinc;
                g += d * dginc;
                b += d * dbinc;
                a += d * dainc;
                z += d * dzinc;
                s += d * dsinc;
                t += d * dtinc;
                w += d * dwinc;

                xend = clipx2;
            }
            if (xstart < clipx1)
            {
                xstart = clipx1;
            }
        }
        else
        {
            if (xstart > clipx2)
            {
                int d = xstart-clipx2;
                r += d * drinc;
                g += d * dginc;
                b += d * dbinc;
                a += d * dainc;
                z += d * dzinc;
                s += d * dsinc;
                t += d * dtinc;
                w += d * dwinc;

                xstart = clipx2;
            }
            if (xend < clipx1)
            {
                xend = clipx1;
            }
        }*/

		xinc = flip ? 1 : -1;
		x = xend;

		length = flip ? (xstart - xend) : (xend - xstart);

		for (j=0; j <= length; j++)
		{
			int sr = r >> 16;
			int sg = g >> 16;
			int sb = b >> 16;
			int sa = a >> 16;
			int ss = s >> 16;
			int st = t >> 16;
			int sw = w >> 16;
			UINT16 sz = z >> 16;
			int oz;
			int sss = 0, sst = 0;
			COLOR c1, c2;
			c1.r = c1.g = c1.b = c1.a = 0;
			c2.r = c2.g = c2.b = c2.a = 0;

			if (x >= clipx1 && x < clipx2)
			{
				if ((z & 0xffff) >= 0x8000) sz++;
				if ((w & 0xffff) >= 0x8000) sw++;

				if (other_modes.persp_tex_en)
				{
					if (sw != 0)
					{
						sss = (((INT64)(ss) << 20) / sw);
						sst = (((INT64)(st) << 20) / sw);
					}
				}
				else
				{
					sss = ss;
					sst = st;
				}

				if (sr > 0xff) sr = 0xff;	if (sg > 0xff) sg = 0xff;	if (sb > 0xff) sb = 0xff;

				shade_color.r = sr; shade_color.g = sg; shade_color.b = sb; shade_color.a = sa;

				if (texture)
				{
					/*if (other_modes.sample_type)
                    {
                        // bilinear

                        sss1 = (sss >> 10) - (tsl >> 2);
                        sss2 = sss1 + 1;

                        sst1 = (sst >> 10) - (ttl >> 2);
                        sst2 = sst1 + 1;

                        CLAMP(sss1, sst1);
                        CLAMP(sss2, sst2);

                        FETCH_TEXEL(&t0, sss1, sst1, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t1, sss2, sst1, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t2, sss1, sst2, twidth, tformat, tsize, tbase);
                        FETCH_TEXEL(&t3, sss2, sst2, twidth, tformat, tsize, tbase);

                        texel0_color.r = (((( (t0.r * (0x3ff - (sss & 0x3ff))) + (t1.r * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.r * (0x3ff - (sss & 0x3ff))) + (t3.r * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.g = (((( (t0.g * (0x3ff - (sss & 0x3ff))) + (t1.g * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.g * (0x3ff - (sss & 0x3ff))) + (t3.g * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.b = (((( (t0.b * (0x3ff - (sss & 0x3ff))) + (t1.b * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.b * (0x3ff - (sss & 0x3ff))) + (t3.b * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);

                        texel0_color.a = (((( (t0.a * (0x3ff - (sss & 0x3ff))) + (t1.a * ((sss & 0x3ff))) ) >> 10) * (0x3ff - (sst & 0x3ff))) >> 10) +
                                         (((( (t2.a * (0x3ff - (sss & 0x3ff))) + (t3.a * ((sss & 0x3ff))) ) >> 10) * ((sst & 0x3ff))) >> 10);
                    }
                    else
                    {
                        sss1 = (sss >> 10) - (tsl >> 2);
                        if ((sss & 0x3ff) >= 0x200) sss1++;

                        sst1 = (sst >> 10) - (ttl >> 2);
                        if ((sst & 0x3ff) >= 0x200) sst1++;

                        CLAMP(sss1, sst1);

                        // point sample
                        FETCH_TEXEL(&texel0_color, sss1, sst1, twidth, tformat, tsize, tbase);
                    }*/

					if (other_modes.cycle_type == CYCLE_TYPE_1)
					{
						TEXTURE_PIPELINE1(sss, sst, 0);
					}
					else
					{
						TEXTURE_PIPELINE2(sss, sst, 0);
					}
				}

				if (other_modes.cycle_type == CYCLE_TYPE_1)
				{
					c1 = COLOR_COMBINER(0);
				}
				else if (other_modes.cycle_type == CYCLE_TYPE_2)
				{
					c1 = COLOR_COMBINER(0);
					c2 = COLOR_COMBINER(1);
				}

				oz = (UINT16)zb[(fb_index + x) ^ WORD_ADDR_XOR];
				if (zbuffer)
				{
					if (sz < oz /*&& c.a != 0*/)
					{
						//BLENDER1_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c);
						{
							int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
							if (other_modes.cycle_type == CYCLE_TYPE_1)
							{
								BLENDER1_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c1, dith);
							}
							else
							{
								BLENDER2_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c1, c2, dith);
							}
						}

						if (other_modes.z_compare_en && other_modes.z_update_en)
						{
							zb[(fb_index + x) ^ WORD_ADDR_XOR] = sz;
						}
					}
				}
				else
				{
					//BLENDER1_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c);

					{
						int dith = dither_matrix_4x4[(((j) & 3) << 2) + ((i^WORD_ADDR_XOR) & 3)];
						if (other_modes.cycle_type == CYCLE_TYPE_1)
						{
							BLENDER1_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c1, dith);
						}
						else
						{
							BLENDER2_16(&fb[(fb_index + x) ^ WORD_ADDR_XOR], c1, c2, dith);
						}
					}
				}
			}

			r += drinc;
			g += dginc;
			b += dbinc;
			a += dainc;
			z += dzinc;
			s += dsinc;
			t += dtinc;
			w += dwinc;

			x += xinc;
		}
	}
}

static void triangle(UINT32 w1, UINT32 w2, int shade, int texture, int zbuffer)
{
	int j;
	int xleft, xright, xleft_inc, xright_inc;
	int xstart, xend;
	int r, g, b, a, z, s, t, w;
	int dr, dg, db, da;
	int drdx = 0, dgdx = 0, dbdx = 0, dadx = 0, dzdx = 0, dsdx = 0, dtdx = 0, dwdx = 0;
	int drdy = 0, dgdy = 0, dbdy = 0, dady = 0, dzdy = 0, dsdy = 0, dtdy = 0, dwdy = 0;
	int drde = 0, dgde = 0, dbde = 0, dade = 0, dzde = 0, dsde = 0, dtde = 0, dwde = 0;
	int tile;
	int flip = (w1 & 0x800000) ? 1 : 0;

	INT32 yl, ym, yh;
	INT32 xl, xm, xh;
	INT64 dxldy, dxhdy, dxmdy;
	UINT32 w3, w4, w5, w6, w7, w8;

	int shade_base = rdp_cmd_cur + 8;
	int texture_base = rdp_cmd_cur + 8;
	int zbuffer_base = rdp_cmd_cur + 8;
	if (shade)
	{
		texture_base += 16;
		zbuffer_base += 16;
	}
	if (texture)
	{
		zbuffer_base += 16;
	}

	w3 = rdp_cmd_data[rdp_cmd_cur+2];
	w4 = rdp_cmd_data[rdp_cmd_cur+3];
	w5 = rdp_cmd_data[rdp_cmd_cur+4];
	w6 = rdp_cmd_data[rdp_cmd_cur+5];
	w7 = rdp_cmd_data[rdp_cmd_cur+6];
	w8 = rdp_cmd_data[rdp_cmd_cur+7];

	yl = (w1 & 0x3fff) >> 2;
	ym = ((w2 >> 16) & 0x3fff) >> 2;
	yh = ((w2 >>  0) & 0x3fff) >> 2;
	xl = (INT32)(w3);
	xh = (INT32)(w5);
	xm = (INT32)(w7);
	dxldy = (INT32)(w4);
	dxhdy = (INT32)(w6);
	dxmdy = (INT32)(w8);

	if (yl & 0x800) yl |= 0xfffff000;
	if (ym & 0x800) ym |= 0xfffff000;
	if (yh & 0x800) yh |= 0xfffff000;

	yl += (w1 >> 1) & 1;
	ym += (w2 >> 17) & 1;
	yh += (w2 >> 1) & 1;

	tile = (w1 >> 16) & 0x7;

	r = 0xff;	g = 0xff;	b = 0xff;	a = 0xff;	z = 0;	s = 0;	t = 0;	w = 0;
	dr = 0;		dg = 0;		db = 0;		da = 0;

	if (shade)
	{
		r    = (rdp_cmd_data[shade_base+0 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+4 ] >> 16) & 0x0000ffff);
		g    = ((rdp_cmd_data[shade_base+0 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+4 ] & 0x0000ffff);
		b    = (rdp_cmd_data[shade_base+1 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+5 ] >> 16) & 0x0000ffff);
		a    = ((rdp_cmd_data[shade_base+1 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+5 ] & 0x0000ffff);
		drdx = (rdp_cmd_data[shade_base+2 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+6 ] >> 16) & 0x0000ffff);
		dgdx = ((rdp_cmd_data[shade_base+2 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+6 ] & 0x0000ffff);
		dbdx = (rdp_cmd_data[shade_base+3 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+7 ] >> 16) & 0x0000ffff);
		dadx = ((rdp_cmd_data[shade_base+3 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+7 ] & 0x0000ffff);
		drde = (rdp_cmd_data[shade_base+8 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+12] >> 16) & 0x0000ffff);
		dgde = ((rdp_cmd_data[shade_base+8 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+12] & 0x0000ffff);
		dbde = (rdp_cmd_data[shade_base+9 ] & 0xffff0000) | ((rdp_cmd_data[shade_base+13] >> 16) & 0x0000ffff);
		dade = ((rdp_cmd_data[shade_base+9 ] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+13] & 0x0000ffff);
		drdy = (rdp_cmd_data[shade_base+10] & 0xffff0000) | ((rdp_cmd_data[shade_base+14] >> 16) & 0x0000ffff);
		dgdy = ((rdp_cmd_data[shade_base+10] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+14] & 0x0000ffff);
		dbdy = (rdp_cmd_data[shade_base+11] & 0xffff0000) | ((rdp_cmd_data[shade_base+15] >> 16) & 0x0000ffff);
		dady = ((rdp_cmd_data[shade_base+11] << 16) & 0xffff0000) | (rdp_cmd_data[shade_base+15] & 0x0000ffff);
	}
	if (texture)
	{
		s    = (rdp_cmd_data[texture_base+0 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+4 ] >> 16) & 0x0000ffff);
		t    = ((rdp_cmd_data[texture_base+0 ] << 16) & 0xffff0000)	| (rdp_cmd_data[texture_base+4 ] & 0x0000ffff);
		w    = (rdp_cmd_data[texture_base+1 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+5 ] >> 16) & 0x0000ffff);
		dsdx = (rdp_cmd_data[texture_base+2 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+6 ] >> 16) & 0x0000ffff);
		dtdx = ((rdp_cmd_data[texture_base+2 ] << 16) & 0xffff0000)	| (rdp_cmd_data[texture_base+6 ] & 0x0000ffff);
		dwdx = (rdp_cmd_data[texture_base+3 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+7 ] >> 16) & 0x0000ffff);
		dsde = (rdp_cmd_data[texture_base+8 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+12] >> 16) & 0x0000ffff);
		dtde = ((rdp_cmd_data[texture_base+8 ] << 16) & 0xffff0000)	| (rdp_cmd_data[texture_base+12] & 0x0000ffff);
		dwde = (rdp_cmd_data[texture_base+9 ] & 0xffff0000) | ((rdp_cmd_data[texture_base+13] >> 16) & 0x0000ffff);
		dsdy = (rdp_cmd_data[texture_base+10] & 0xffff0000) | ((rdp_cmd_data[texture_base+14] >> 16) & 0x0000ffff);
		dtdy = ((rdp_cmd_data[texture_base+10] << 16) & 0xffff0000)	| (rdp_cmd_data[texture_base+14] & 0x0000ffff);
		dwdy = (rdp_cmd_data[texture_base+11] & 0xffff0000) | ((rdp_cmd_data[texture_base+15] >> 16) & 0x0000ffff);
	}
	if (zbuffer)
	{
		z    = rdp_cmd_data[zbuffer_base+0];
		dzdx = rdp_cmd_data[zbuffer_base+1];
		dzde = rdp_cmd_data[zbuffer_base+2];
		dzdy = rdp_cmd_data[zbuffer_base+3];
	}

	xleft = xm;
	xright = xh;
	xleft_inc = dxmdy;
	xright_inc = dxhdy;

	for (j=yh; j < ym; j++)
	{
		xstart = xleft >> 16;
		xend = xright >> 16;

		if (j >= 0)
		{
			span[j].lx = xstart;
			span[j].rx = xend;
			span[j].s = s;		span[j].ds = dsdx;
			span[j].t = t;		span[j].dt = dtdx;
			span[j].w = w;		span[j].dw = dwdx;
			span[j].r = r;		span[j].dr = drdx;
			span[j].g = g;		span[j].dg = dgdx;
			span[j].b = b;		span[j].db = dbdx;
			span[j].a = a;		span[j].da = dadx;
			span[j].z = z;		span[j].dz = dzdx;
		}

		xleft += xleft_inc;
		xright += xright_inc;
		s += dsde;
		t += dtde;
		w += dwde;
		r += drde;
		g += dgde;
		b += dbde;
		a += dade;
		z += dzde;
	}

	xleft = xl;
	xleft_inc = dxldy;
	xright_inc = dxhdy;

	for (j=ym; j <= yl; j++)
	{
		xstart = xleft >> 16;
		xend = xright >> 16;

		if (j >= 0)
		{
			span[j].lx = xstart;
			span[j].rx = xend;
			span[j].s = s;		span[j].ds = dsdx;
			span[j].t = t;		span[j].dt = dtdx;
			span[j].w = w;		span[j].dw = dwdx;
			span[j].r = r;		span[j].dr = drdx;
			span[j].g = g;		span[j].dg = dgdx;
			span[j].b = b;		span[j].db = dbdx;
			span[j].a = a;		span[j].da = dadx;
			span[j].z = z;		span[j].dz = dzdx;
		}

		xleft += xleft_inc;
		xright += xright_inc;
		s += dsde;
		t += dtde;
		w += dwde;
		r += drde;
		g += dgde;
		b += dbde;
		a += dade;
		z += dzde;
	}

	switch (fb_size)
	{
		case PIXEL_SIZE_16BIT:	render_spans_16(yh, yl, tile, shade, texture, zbuffer, flip); break;
		case PIXEL_SIZE_32BIT:	render_spans_32(yh, yl, tile, shade, texture, zbuffer, flip); break;
	}
}

/*****************************************************************************/

INLINE UINT32 READ_RDP_DATA(UINT32 address)
{
	if (dp_status & 0x1)		// XBUS_DMEM_DMA enabled
	{
		return rsp_dmem[(address & 0xfff) / 4];
	}
	else
	{
		return rdram[(address / 4)];
	}
}

static const char *image_format[] = { "RGBA", "YUV", "CI", "IA", "I", "???", "???", "???" };
static const char *image_size[] = { "4-bit", "8-bit", "16-bit", "32-bit" };

static const int rdp_command_length[64] =
{
	8,			// 0x00, No Op
	8,			// 0x01, ???
	8,			// 0x02, ???
	8,			// 0x03, ???
	8,			// 0x04, ???
	8,			// 0x05, ???
	8,			// 0x06, ???
	8,			// 0x07, ???
	32,			// 0x08, Non-Shaded Triangle
	32+16,		// 0x09, Non-Shaded, Z-Buffered Triangle
	32+64,		// 0x0a, Textured Triangle
	32+64+16,	// 0x0b, Textured, Z-Buffered Triangle
	32+64,		// 0x0c, Shaded Triangle
	32+64+16,	// 0x0d, Shaded, Z-Buffered Triangle
	32+64+64,	// 0x0e, Shaded+Textured Triangle
	32+64+64+16,// 0x0f, Shaded+Textured, Z-Buffered Triangle
	8,			// 0x10, ???
	8,			// 0x11, ???
	8,			// 0x12, ???
	8,			// 0x13, ???
	8,			// 0x14, ???
	8,			// 0x15, ???
	8,			// 0x16, ???
	8,			// 0x17, ???
	8,			// 0x18, ???
	8,			// 0x19, ???
	8,			// 0x1a, ???
	8,			// 0x1b, ???
	8,			// 0x1c, ???
	8,			// 0x1d, ???
	8,			// 0x1e, ???
	8,			// 0x1f, ???
	8,			// 0x20, ???
	8,			// 0x21, ???
	8,			// 0x22, ???
	8,			// 0x23, ???
	16,			// 0x24, Texture_Rectangle
	16,			// 0x25, Texture_Rectangle_Flip
	8,			// 0x26, Sync_Load
	8,			// 0x27, Sync_Pipe
	8,			// 0x28, Sync_Tile
	8,			// 0x29, Sync_Full
	8,			// 0x2a, Set_Key_GB
	8,			// 0x2b, Set_Key_R
	8,			// 0x2c, Set_Convert
	8,			// 0x2d, Set_Scissor
	8,			// 0x2e, Set_Prim_Depth
	8,			// 0x2f, Set_Other_Modes
	8,			// 0x30, Load_TLUT
	8,			// 0x31, ???
	8,			// 0x32, Set_Tile_Size
	8,			// 0x33, Load_Block
	8,			// 0x34, Load_Tile
	8,			// 0x35, Set_Tile
	8,			// 0x36, Fill_Rectangle
	8,			// 0x37, Set_Fill_Color
	8,			// 0x38, Set_Fog_Color
	8,			// 0x39, Set_Blend_Color
	8,			// 0x3a, Set_Prim_Color
	8,			// 0x3b, Set_Env_Color
	8,			// 0x3c, Set_Combine
	8,			// 0x3d, Set_Texture_Image
	8,			// 0x3e, Set_Mask_Image
	8			// 0x3f, Set_Color_Image
};

static int rdp_dasm(char *buffer)
{
	int i;
	int tile;
	const char *format, *size;
	char sl[32], tl[32], sh[32], th[32];
	char s[32], t[32], w[32];
	char dsdx[32], dtdx[32], dwdx[32];
	char dsdy[32], dtdy[32], dwdy[32];
	char dsde[32], dtde[32], dwde[32];
	char yl[32], yh[32], ym[32], xl[32], xh[32], xm[32];
	char dxldy[32], dxhdy[32], dxmdy[32];
	char rt[32], gt[32], bt[32], at[32];
	char drdx[32], dgdx[32], dbdx[32], dadx[32];
	char drdy[32], dgdy[32], dbdy[32], dady[32];
	char drde[32], dgde[32], dbde[32], dade[32];
	UINT32 r,g,b,a;

	UINT32 cmd[64];
	UINT32 length;
	UINT32 command;

	length = rdp_cmd_ptr * 4;
	if (length < 8)
	{
		sprintf(buffer, "ERROR: length = %d\n", length);
		return 0;
	}

	cmd[0] = rdp_cmd_data[rdp_cmd_cur+0];
	cmd[1] = rdp_cmd_data[rdp_cmd_cur+1];

	tile = (cmd[1] >> 24) & 0x7;
	sprintf(sl, "%4.2f", (float)((cmd[0] >> 12) & 0xfff) / 4.0f);
	sprintf(tl, "%4.2f", (float)((cmd[0] >>  0) & 0xfff) / 4.0f);
	sprintf(sh, "%4.2f", (float)((cmd[1] >> 12) & 0xfff) / 4.0f);
	sprintf(th, "%4.2f", (float)((cmd[1] >>  0) & 0xfff) / 4.0f);

	format = image_format[(cmd[0] >> 21) & 0x7];
	size = image_size[(cmd[0] >> 19) & 0x3];

	r = (cmd[1] >> 24) & 0xff;
	g = (cmd[1] >> 16) & 0xff;
	b = (cmd[1] >>  8) & 0xff;
	a = (cmd[1] >>  0) & 0xff;

	command = (cmd[0] >> 24) & 0x3f;
	switch (command)
	{
		case 0x00:	sprintf(buffer, "No Op"); break;
		case 0x08:		// Tri_NoShade
		{
			int lft = (command >> 23) & 0x1;

			if (length != rdp_command_length[command])
			{
				sprintf(buffer, "ERROR: Tri_NoShade length = %d\n", length);
				return 0;
			}

			cmd[2] = rdp_cmd_data[rdp_cmd_cur+2];
			cmd[3] = rdp_cmd_data[rdp_cmd_cur+3];
			cmd[4] = rdp_cmd_data[rdp_cmd_cur+4];
			cmd[5] = rdp_cmd_data[rdp_cmd_cur+5];
			cmd[6] = rdp_cmd_data[rdp_cmd_cur+6];
			cmd[7] = rdp_cmd_data[rdp_cmd_cur+7];

			sprintf(yl,		"%4.4f", (float)((cmd[0] >>  0) & 0x1fff) / 4.0f);
			sprintf(ym,		"%4.4f", (float)((cmd[1] >> 16) & 0x1fff) / 4.0f);
			sprintf(yh,		"%4.4f", (float)((cmd[1] >>  0) & 0x1fff) / 4.0f);
			sprintf(xl,		"%4.4f", (float)(cmd[2] / 65536.0f));
			sprintf(dxldy,	"%4.4f", (float)(cmd[3] / 65536.0f));
			sprintf(xh,		"%4.4f", (float)(cmd[4] / 65536.0f));
			sprintf(dxhdy,	"%4.4f", (float)(cmd[5] / 65536.0f));
			sprintf(xm,		"%4.4f", (float)(cmd[6] / 65536.0f));
			sprintf(dxmdy,	"%4.4f", (float)(cmd[7] / 65536.0f));

					sprintf(buffer, "Tri_NoShade            %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh);
			break;
		}
		case 0x0a:		// Tri_Tex
		{
			int lft = (command >> 23) & 0x1;

			if (length < rdp_command_length[command])
			{
				sprintf(buffer, "ERROR: Tri_Tex length = %d\n", length);
				return 0;
			}

			for (i=2; i < 24; i++)
			{
				cmd[i] = rdp_cmd_data[rdp_cmd_cur+i];
			}

			sprintf(yl,		"%4.4f", (float)((cmd[0] >>  0) & 0x1fff) / 4.0f);
			sprintf(ym,		"%4.4f", (float)((cmd[1] >> 16) & 0x1fff) / 4.0f);
			sprintf(yh,		"%4.4f", (float)((cmd[1] >>  0) & 0x1fff) / 4.0f);
			sprintf(xl,		"%4.4f", (float)((INT32)cmd[2] / 65536.0f));
			sprintf(dxldy,	"%4.4f", (float)((INT32)cmd[3] / 65536.0f));
			sprintf(xh,		"%4.4f", (float)((INT32)cmd[4] / 65536.0f));
			sprintf(dxhdy,	"%4.4f", (float)((INT32)cmd[5] / 65536.0f));
			sprintf(xm,		"%4.4f", (float)((INT32)cmd[6] / 65536.0f));
			sprintf(dxmdy,	"%4.4f", (float)((INT32)cmd[7] / 65536.0f));

			sprintf(s,		"%4.4f", (float)(INT32)((cmd[ 8] & 0xffff0000) | ((cmd[12] >> 16) & 0xffff)) / 65536.0f);
			sprintf(t,		"%4.4f", (float)(INT32)(((cmd[ 8] & 0xffff) << 16) | (cmd[12] & 0xffff)) / 65536.0f);
			sprintf(w,		"%4.4f", (float)(INT32)((cmd[ 9] & 0xffff0000) | ((cmd[13] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsdx,	"%4.4f", (float)(INT32)((cmd[10] & 0xffff0000) | ((cmd[14] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtdx,	"%4.4f", (float)(INT32)(((cmd[10] & 0xffff) << 16) | (cmd[14] & 0xffff)) / 65536.0f);
			sprintf(dwdx,	"%4.4f", (float)(INT32)((cmd[11] & 0xffff0000) | ((cmd[15] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsde,	"%4.4f", (float)(INT32)((cmd[16] & 0xffff0000) | ((cmd[20] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtde,	"%4.4f", (float)(INT32)(((cmd[16] & 0xffff) << 16) | (cmd[20] & 0xffff)) / 65536.0f);
			sprintf(dwde,	"%4.4f", (float)(INT32)((cmd[17] & 0xffff0000) | ((cmd[21] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsdy,	"%4.4f", (float)(INT32)((cmd[18] & 0xffff0000) | ((cmd[22] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtdy,	"%4.4f", (float)(INT32)(((cmd[18] & 0xffff) << 16) | (cmd[22] & 0xffff)) / 65536.0f);
			sprintf(dwdy,	"%4.4f", (float)(INT32)((cmd[19] & 0xffff0000) | ((cmd[23] >> 16) & 0xffff)) / 65536.0f);


			buffer+=sprintf(buffer, "Tri_Tex               %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       S: %s, T: %s, W: %s\n", s, t, w);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy);
			break;
		}
		case 0x0c:		// Tri_Shade
		{
			int lft = (command >> 23) & 0x1;

			if (length != rdp_command_length[command])
			{
				sprintf(buffer, "ERROR: Tri_Shade length = %d\n", length);
				return 0;
			}

			for (i=2; i < 24; i++)
			{
				cmd[i] = rdp_cmd_data[i];
			}

			sprintf(yl,		"%4.4f", (float)((cmd[0] >>  0) & 0x1fff) / 4.0f);
			sprintf(ym,		"%4.4f", (float)((cmd[1] >> 16) & 0x1fff) / 4.0f);
			sprintf(yh,		"%4.4f", (float)((cmd[1] >>  0) & 0x1fff) / 4.0f);
			sprintf(xl,		"%4.4f", (float)((INT32)cmd[2] / 65536.0f));
			sprintf(dxldy,	"%4.4f", (float)((INT32)cmd[3] / 65536.0f));
			sprintf(xh,		"%4.4f", (float)((INT32)cmd[4] / 65536.0f));
			sprintf(dxhdy,	"%4.4f", (float)((INT32)cmd[5] / 65536.0f));
			sprintf(xm,		"%4.4f", (float)((INT32)cmd[6] / 65536.0f));
			sprintf(dxmdy,	"%4.4f", (float)((INT32)cmd[7] / 65536.0f));
			sprintf(rt,		"%4.4f", (float)(INT32)((cmd[8] & 0xffff0000) | ((cmd[12] >> 16) & 0xffff)) / 65536.0f);
			sprintf(gt,		"%4.4f", (float)(INT32)(((cmd[8] & 0xffff) << 16) | (cmd[12] & 0xffff)) / 65536.0f);
			sprintf(bt,		"%4.4f", (float)(INT32)((cmd[9] & 0xffff0000) | ((cmd[13] >> 16) & 0xffff)) / 65536.0f);
			sprintf(at,		"%4.4f", (float)(INT32)(((cmd[9] & 0xffff) << 16) | (cmd[13] & 0xffff)) / 65536.0f);
			sprintf(drdx,	"%4.4f", (float)(INT32)((cmd[10] & 0xffff0000) | ((cmd[14] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgdx,	"%4.4f", (float)(INT32)(((cmd[10] & 0xffff) << 16) | (cmd[14] & 0xffff)) / 65536.0f);
			sprintf(dbdx,	"%4.4f", (float)(INT32)((cmd[11] & 0xffff0000) | ((cmd[15] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dadx,	"%4.4f", (float)(INT32)(((cmd[11] & 0xffff) << 16) | (cmd[15] & 0xffff)) / 65536.0f);
			sprintf(drde,	"%4.4f", (float)(INT32)((cmd[16] & 0xffff0000) | ((cmd[20] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgde,	"%4.4f", (float)(INT32)(((cmd[16] & 0xffff) << 16) | (cmd[20] & 0xffff)) / 65536.0f);
			sprintf(dbde,	"%4.4f", (float)(INT32)((cmd[17] & 0xffff0000) | ((cmd[21] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dade,	"%4.4f", (float)(INT32)(((cmd[17] & 0xffff) << 16) | (cmd[21] & 0xffff)) / 65536.0f);
			sprintf(drdy,	"%4.4f", (float)(INT32)((cmd[18] & 0xffff0000) | ((cmd[22] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgdy,	"%4.4f", (float)(INT32)(((cmd[18] & 0xffff) << 16) | (cmd[22] & 0xffff)) / 65536.0f);
			sprintf(dbdy,	"%4.4f", (float)(INT32)((cmd[19] & 0xffff0000) | ((cmd[23] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dady,	"%4.4f", (float)(INT32)(((cmd[19] & 0xffff) << 16) | (cmd[23] & 0xffff)) / 65536.0f);

			buffer+=sprintf(buffer, "Tri_Shade              %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady);
			break;
		}
		case 0x0e:		// Tri_TexShade
		{
			int lft = (command >> 23) & 0x1;

			if (length < rdp_command_length[command])
			{
				sprintf(buffer, "ERROR: Tri_TexShade length = %d\n", length);
				return 0;
			}

			for (i=2; i < 40; i++)
			{
				cmd[i] = rdp_cmd_data[rdp_cmd_cur+i];
			}

			sprintf(yl,		"%4.4f", (float)((cmd[0] >>  0) & 0x1fff) / 4.0f);
			sprintf(ym,		"%4.4f", (float)((cmd[1] >> 16) & 0x1fff) / 4.0f);
			sprintf(yh,		"%4.4f", (float)((cmd[1] >>  0) & 0x1fff) / 4.0f);
			sprintf(xl,		"%4.4f", (float)((INT32)cmd[2] / 65536.0f));
			sprintf(dxldy,	"%4.4f", (float)((INT32)cmd[3] / 65536.0f));
			sprintf(xh,		"%4.4f", (float)((INT32)cmd[4] / 65536.0f));
			sprintf(dxhdy,	"%4.4f", (float)((INT32)cmd[5] / 65536.0f));
			sprintf(xm,		"%4.4f", (float)((INT32)cmd[6] / 65536.0f));
			sprintf(dxmdy,	"%4.4f", (float)((INT32)cmd[7] / 65536.0f));
			sprintf(rt,		"%4.4f", (float)(INT32)((cmd[8] & 0xffff0000) | ((cmd[12] >> 16) & 0xffff)) / 65536.0f);
			sprintf(gt,		"%4.4f", (float)(INT32)(((cmd[8] & 0xffff) << 16) | (cmd[12] & 0xffff)) / 65536.0f);
			sprintf(bt,		"%4.4f", (float)(INT32)((cmd[9] & 0xffff0000) | ((cmd[13] >> 16) & 0xffff)) / 65536.0f);
			sprintf(at,		"%4.4f", (float)(INT32)(((cmd[9] & 0xffff) << 16) | (cmd[13] & 0xffff)) / 65536.0f);
			sprintf(drdx,	"%4.4f", (float)(INT32)((cmd[10] & 0xffff0000) | ((cmd[14] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgdx,	"%4.4f", (float)(INT32)(((cmd[10] & 0xffff) << 16) | (cmd[14] & 0xffff)) / 65536.0f);
			sprintf(dbdx,	"%4.4f", (float)(INT32)((cmd[11] & 0xffff0000) | ((cmd[15] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dadx,	"%4.4f", (float)(INT32)(((cmd[11] & 0xffff) << 16) | (cmd[15] & 0xffff)) / 65536.0f);
			sprintf(drde,	"%4.4f", (float)(INT32)((cmd[16] & 0xffff0000) | ((cmd[20] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgde,	"%4.4f", (float)(INT32)(((cmd[16] & 0xffff) << 16) | (cmd[20] & 0xffff)) / 65536.0f);
			sprintf(dbde,	"%4.4f", (float)(INT32)((cmd[17] & 0xffff0000) | ((cmd[21] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dade,	"%4.4f", (float)(INT32)(((cmd[17] & 0xffff) << 16) | (cmd[21] & 0xffff)) / 65536.0f);
			sprintf(drdy,	"%4.4f", (float)(INT32)((cmd[18] & 0xffff0000) | ((cmd[22] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dgdy,	"%4.4f", (float)(INT32)(((cmd[18] & 0xffff) << 16) | (cmd[22] & 0xffff)) / 65536.0f);
			sprintf(dbdy,	"%4.4f", (float)(INT32)((cmd[19] & 0xffff0000) | ((cmd[23] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dady,	"%4.4f", (float)(INT32)(((cmd[19] & 0xffff) << 16) | (cmd[23] & 0xffff)) / 65536.0f);

			sprintf(s,		"%4.4f", (float)(INT32)((cmd[24] & 0xffff0000) | ((cmd[28] >> 16) & 0xffff)) / 65536.0f);
			sprintf(t,		"%4.4f", (float)(INT32)(((cmd[24] & 0xffff) << 16) | (cmd[28] & 0xffff)) / 65536.0f);
			sprintf(w,		"%4.4f", (float)(INT32)((cmd[25] & 0xffff0000) | ((cmd[29] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsdx,	"%4.4f", (float)(INT32)((cmd[26] & 0xffff0000) | ((cmd[30] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtdx,	"%4.4f", (float)(INT32)(((cmd[26] & 0xffff) << 16) | (cmd[30] & 0xffff)) / 65536.0f);
			sprintf(dwdx,	"%4.4f", (float)(INT32)((cmd[27] & 0xffff0000) | ((cmd[31] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsde,	"%4.4f", (float)(INT32)((cmd[32] & 0xffff0000) | ((cmd[36] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtde,	"%4.4f", (float)(INT32)(((cmd[32] & 0xffff) << 16) | (cmd[36] & 0xffff)) / 65536.0f);
			sprintf(dwde,	"%4.4f", (float)(INT32)((cmd[33] & 0xffff0000) | ((cmd[37] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dsdy,	"%4.4f", (float)(INT32)((cmd[34] & 0xffff0000) | ((cmd[38] >> 16) & 0xffff)) / 65536.0f);
			sprintf(dtdy,	"%4.4f", (float)(INT32)(((cmd[34] & 0xffff) << 16) | (cmd[38] & 0xffff)) / 65536.0f);
			sprintf(dwdy,	"%4.4f", (float)(INT32)((cmd[35] & 0xffff0000) | ((cmd[39] >> 16) & 0xffff)) / 65536.0f);


			buffer+=sprintf(buffer, "Tri_TexShade           %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady);

			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       S: %s, T: %s, W: %s\n", s, t, w);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde);
			buffer+=sprintf(buffer, "                              ");
			buffer+=sprintf(buffer, "                       DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy);
			break;
		}
		case 0x24:
		case 0x25:
		{
			if (length < 16)
			{
				sprintf(buffer, "ERROR: Texture_Rectangle length = %d\n", length);
				return 0;
			}
			cmd[2] = rdp_cmd_data[rdp_cmd_cur+2];
			cmd[3] = rdp_cmd_data[rdp_cmd_cur+3];
			sprintf(s,    "%4.4f", (float)(INT16)((cmd[2] >> 16) & 0xffff) / 32.0f);
			sprintf(t,    "%4.4f", (float)(INT16)((cmd[2] >>  0) & 0xffff) / 32.0f);
			sprintf(dsdx, "%4.4f", (float)(INT16)((cmd[3] >> 16) & 0xffff) / 1024.0f);
			sprintf(dtdy, "%4.4f", (float)(INT16)((cmd[3] >> 16) & 0xffff) / 1024.0f);

			if (command == 0x24)
					sprintf(buffer, "Texture_Rectangle      %d, %s, %s, %s, %s,  %s, %s, %s, %s", tile, sh, th, sl, tl, s, t, dsdx, dtdy);
			else
					sprintf(buffer, "Texture_Rectangle_Flip %d, %s, %s, %s, %s,  %s, %s, %s, %s", tile, sh, th, sl, tl, s, t, dsdx, dtdy);

			break;
		}
		case 0x26:	sprintf(buffer, "Sync_Load"); break;
		case 0x27:	sprintf(buffer, "Sync_Pipe"); break;
		case 0x28:	sprintf(buffer, "Sync_Tile"); break;
		case 0x29:	sprintf(buffer, "Sync_Full"); break;
		case 0x2d:	sprintf(buffer, "Set_Scissor            %s, %s, %s, %s", sl, tl, sh, th); break;
		case 0x2e:	sprintf(buffer, "Set_Prim_Depth         %04X, %04X", (cmd[1] >> 16) & 0xffff, cmd[1] & 0xffff); break;
		case 0x2f:	sprintf(buffer, "Set_Other_Modes        %08X %08X", cmd[0], cmd[1]); break;
		case 0x30:	sprintf(buffer, "Load_TLUT              %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break;
		case 0x32:	sprintf(buffer, "Set_Tile_Size          %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break;
		case 0x33:	sprintf(buffer, "Load_Block             %d, %03X, %03X, %03X, %03X", tile, (cmd[0] >> 12) & 0xfff, cmd[0] & 0xfff, (cmd[1] >> 12) & 0xfff, cmd[1] & 0xfff); break;
		case 0x34:	sprintf(buffer, "Load_Tile              %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break;
		case 0x35:	sprintf(buffer, "Set_Tile               %d, %s, %s, %d, %04X", tile, format, size, ((cmd[0] >> 9) & 0x1ff) * 8, (cmd[0] & 0x1ff) * 8); break;
		case 0x36:	sprintf(buffer, "Fill_Rectangle         %s, %s, %s, %s", sh, th, sl, tl); break;
		case 0x37:	sprintf(buffer, "Set_Fill_Color         R: %d, G: %d, B: %d, A: %d", r, g, b, a); break;
		case 0x38:	sprintf(buffer, "Set_Fog_Color          R: %d, G: %d, B: %d, A: %d", r, g, b, a); break;
		case 0x39:	sprintf(buffer, "Set_Blend_Color        R: %d, G: %d, B: %d, A: %d", r, g, b, a); break;
		case 0x3a:	sprintf(buffer, "Set_Prim_Color         %d, %d, R: %d, G: %d, B: %d, A: %d", (cmd[0] >> 8) & 0x1f, cmd[0] & 0xff, r, g, b, a); break;
		case 0x3b:	sprintf(buffer, "Set_Env_Color          R: %d, G: %d, B: %d, A: %d", r, g, b, a); break;
		case 0x3c:	sprintf(buffer, "Set_Combine            %08X %08X", cmd[0], cmd[1]); break;
		case 0x3d:	sprintf(buffer, "Set_Texture_Image      %s, %s, %d, %08X", format, size, (cmd[0] & 0x1ff)+1, cmd[1]); break;
		case 0x3e:	sprintf(buffer, "Set_Mask_Image         %08X", cmd[1]); break;
		case 0x3f:	sprintf(buffer, "Set_Color_Image        %s, %s, %d, %08X", format, size, (cmd[0] & 0x1ff)+1, cmd[1]); break;
		default:	sprintf(buffer, "??? (%08X %08X)", cmd[0], cmd[1]); break;
	}

	return rdp_command_length[command];
}

/*****************************************************************************/

////////////////////////
// RDP COMMANDS
////////////////////////

static void rdp_invalid(UINT32 w1, UINT32 w2)
{
	fatalerror("RDP: invalid command  %d, %08X %08X\n", (w1 >> 24) & 0x3f, w1, w2);
}

static void rdp_noop(UINT32 w1, UINT32 w2)
{

}

static void rdp_tri_noshade(UINT32 w1, UINT32 w2)
{
	//fatalerror("RDP: unhandled command tri_noshade, %08X %08X\n", w1, w2);
	triangle(w1, w2, 0, 0, 0);
}

static void rdp_tri_noshade_z(UINT32 w1, UINT32 w2)
{
	//fatalerror("RDP: unhandled command tri_noshade_z, %08X %08X\n", w1, w2);
	triangle(w1, w2, 0, 0, 1);
}

static void rdp_tri_tex(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_tex, %08X %08X\n", w1, w2);

	triangle(w1, w2, 0, 1, 0);
}

static void rdp_tri_tex_z(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_tex_z, %08X %08X\n", w1, w2);

	triangle(w1, w2, 0, 1, 1);
}

static void rdp_tri_shade(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_shade, %08X %08X\n", w1, w2);

	triangle(w1, w2, 1, 0, 0);
}

static void rdp_tri_shade_z(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_shade_z, %08X %08X\n", w1, w2);

	triangle(w1, w2, 1, 0, 1);
}

static void rdp_tri_texshade(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_texshade, %08X %08X\n", w1, w2);

	triangle(w1, w2, 1, 1, 0);
}

static void rdp_tri_texshade_z(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command tri_texshade_z, %08X %08X\n", w1, w2);

	triangle(w1, w2, 1, 1, 1);
}

static void rdp_tex_rect(UINT32 w1, UINT32 w2)
{
	UINT32 w3, w4;
	TEX_RECTANGLE rect;

	w3 = rdp_cmd_data[rdp_cmd_cur+2];
	w4 = rdp_cmd_data[rdp_cmd_cur+3];

	rect.tilenum	= (w2 >> 24) & 0x7;
	rect.xl			= (w1 >> 12) & 0xfff;
	rect.yl			= (w1 >>  0) & 0xfff;
	rect.xh			= (w2 >> 12) & 0xfff;
	rect.yh			= (w2 >>  0) & 0xfff;
	rect.s			= (w3 >> 16) & 0xffff;
	rect.t			= (w3 >>  0) & 0xffff;
	rect.dsdx		= (w4 >> 16) & 0xffff;
	rect.dtdy		= (w4 >>  0) & 0xffff;

	switch (fb_size)
	{
		case PIXEL_SIZE_16BIT:		texture_rectangle_16bit(&rect); break;
		case PIXEL_SIZE_32BIT:		texture_rectangle_32bit(&rect); break;
	}
}

static void rdp_tex_rect_flip(UINT32 w1, UINT32 w2)
{
	UINT32 w3, w4;
	TEX_RECTANGLE rect;

	w3 = rdp_cmd_data[rdp_cmd_cur+2];
	w4 = rdp_cmd_data[rdp_cmd_cur+3];

	rect.tilenum	= (w2 >> 24) & 0x7;
	rect.xl			= (w1 >> 12) & 0xfff;
	rect.yl			= (w1 >>  0) & 0xfff;
	rect.xh			= (w2 >> 12) & 0xfff;
	rect.yh			= (w2 >>  0) & 0xfff;
	rect.t			= (w3 >> 16) & 0xffff;
	rect.s			= (w3 >>  0) & 0xffff;
	rect.dtdy		= (w4 >> 16) & 0xffff;
	rect.dsdx		= (w4 >>  0) & 0xffff;

	switch (fb_size)
	{
		case PIXEL_SIZE_16BIT:		texture_rectangle_16bit(&rect); break;
		case PIXEL_SIZE_32BIT:		texture_rectangle_32bit(&rect); break;
	}
}

static void rdp_sync_load(UINT32 w1, UINT32 w2)
{
	// Nothing to do?
}

static void rdp_sync_pipe(UINT32 w1, UINT32 w2)
{
	// Nothing to do?
}

static void rdp_sync_tile(UINT32 w1, UINT32 w2)
{
	// Nothing to do?
}

static void rdp_sync_full(UINT32 w1, UINT32 w2)
{
  // VP ?
	//dp_full_sync();
  printf("full sync\n");
  rdp_update();

  *gfx.MI_INTR_REG |= 0x20;
  gfx.CheckInterrupts();
  
}

static void rdp_set_key_gb(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command set_key_gb, %08X %08X\n", w1, w2);
}

static void rdp_set_key_r(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command set_key_r, %08X %08X\n", w1, w2);
}

static void rdp_set_convert(UINT32 w1, UINT32 w2)
{
	//osd_die("RDP: unhandled command set_convert, %08X %08X\n", w1, w2);
}

static void rdp_set_scissor(UINT32 w1, UINT32 w2)
{
	clip.xh = (w1 >> 12) & 0xfff;
	clip.yh = (w1 >>  0) & 0xfff;
	clip.xl = (w2 >> 12) & 0xfff;
	clip.yl = (w2 >>  0) & 0xfff;

	// TODO: handle f & o?
}

static void rdp_set_prim_depth(UINT32 w1, UINT32 w2)
{
	primitive_z = (UINT16)(w2 >> 16);
	primitive_delta_z = (UINT16)(w1);
}

static void rdp_set_other_modes(UINT32 w1, UINT32 w2)
{
	other_modes.cycle_type			= (w1 >> 20) & 0x3;
	other_modes.persp_tex_en 		= (w1 & 0x80000) ? 1 : 0;
	other_modes.detail_tex_en		= (w1 & 0x40000) ? 1 : 0;
	other_modes.sharpen_tex_en		= (w1 & 0x20000) ? 1 : 0;
	other_modes.tex_lod_en			= (w1 & 0x10000) ? 1 : 0;
	other_modes.en_tlut				= (w1 & 0x08000) ? 1 : 0;
	other_modes.tlut_type			= (w1 & 0x04000) ? 1 : 0;
	other_modes.sample_type			= (w1 & 0x02000) ? 1 : 0;
	other_modes.mid_texel			= (w1 & 0x01000) ? 1 : 0;
	other_modes.bi_lerp0			= (w1 & 0x00800) ? 1 : 0;
	other_modes.bi_lerp1			= (w1 & 0x00400) ? 1 : 0;
	other_modes.convert_one			= (w1 & 0x00200) ? 1 : 0;
	other_modes.key_en				= (w1 & 0x00100) ? 1 : 0;
	other_modes.rgb_dither_sel		= (w1 >> 6) & 0x3;
	other_modes.alpha_dither_sel	= (w1 >> 4) & 0x3;
	other_modes.blend_m1a_0			= (w2 >> 30) & 0x3;
	other_modes.blend_m1a_1			= (w2 >> 28) & 0x3;
	other_modes.blend_m1b_0			= (w2 >> 26) & 0x3;
	other_modes.blend_m1b_1			= (w2 >> 24) & 0x3;
	other_modes.blend_m2a_0			= (w2 >> 22) & 0x3;
	other_modes.blend_m2a_1			= (w2 >> 20) & 0x3;
	other_modes.blend_m2b_0			= (w2 >> 18) & 0x3;
	other_modes.blend_m2b_1			= (w2 >> 16) & 0x3;
	other_modes.force_blend			= (w2 & 0x4000) ? 1 : 0;
	other_modes.alpha_cvg_select	= (w2 & 0x2000) ? 1 : 0;
	other_modes.cvg_times_alpha		= (w2 & 0x1000) ? 1 : 0;
	other_modes.z_mode				= (w2 >> 10) & 0x3;
	other_modes.cvg_dest			= (w2 >> 8) & 0x3;
	other_modes.color_on_cvg		= (w2 & 0x80) ? 1 : 0;
	other_modes.image_read_en		= (w2 & 0x40) ? 1 : 0;
	other_modes.z_update_en			= (w2 & 0x20) ? 1 : 0;
	other_modes.z_compare_en		= (w2 & 0x10) ? 1 : 0;
	other_modes.antialias_en		= (w2 & 0x08) ? 1 : 0;
	other_modes.z_source_sel		= (w2 & 0x04) ? 1 : 0;
	other_modes.dither_alpha_en		= (w2 & 0x02) ? 1 : 0;
	other_modes.alpha_compare_en	= (w2 & 0x01) ? 1 : 0;

	SET_BLENDER_INPUT(0, 0, &blender1a_r[0], &blender1a_g[0], &blender1a_b[0], &blender1b_a[0],
					  other_modes.blend_m1a_0, other_modes.blend_m1b_0);
	SET_BLENDER_INPUT(0, 1, &blender2a_r[0], &blender2a_g[0], &blender2a_b[0], &blender2b_a[0],
					  other_modes.blend_m2a_0, other_modes.blend_m2b_0);
	SET_BLENDER_INPUT(1, 0, &blender1a_r[1], &blender1a_g[1], &blender1a_b[1], &blender1b_a[1],
					  other_modes.blend_m1a_1, other_modes.blend_m1b_1);
	SET_BLENDER_INPUT(1, 1, &blender2a_r[1], &blender2a_g[1], &blender2a_b[1], &blender2b_a[1],
					  other_modes.blend_m2a_1, other_modes.blend_m2b_1);

//  mame_printf_debug("blend: m1a = %d, m1b = %d, m2a = %d, m2b = %d\n", other_modes.blend_m1a_0, other_modes.blend_m1b_0, other_modes.blend_m2a_0, other_modes.blend_m2b_0);

	switch (other_modes.rgb_dither_sel)
	{
		case 0: break;
		case 1: break; //fatalerror("bayer matrix dither\n"); break;
		case 2: break; /////fatalerror("noise dither\n"); break;
		case 3: break;
	}
}

static void rdp_load_tlut(UINT32 w1, UINT32 w2)
{
	int i;
	UINT16 sl, sh;
//  int tilenum = (w2 >> 24) & 0x7;

	sl	= ((w1 >> 12) & 0xfff) / 4;
	sh	= ((w2 >> 12) & 0xfff) / 4;

	switch (ti_format)
	{
		case 0:		// RGBA
		{
			switch (ti_size)
			{
				case PIXEL_SIZE_16BIT:
				{
					UINT16 *src = (UINT16*)&rdram[ti_address / 4];
	//              UINT16 *tlut = (UINT16*)&texture_cache[tile[tilenum].tmem/2];

					for (i=sl; i <= sh; i++)
					{
						tlut[i] = src[i];
			//          mame_printf_debug("TLUT %d = %04X\n", i, tlut[i]);
					}
					break;
				}
				default:	fatalerror("RDP: load_tlut: size = %d\n", ti_size);
			}
			break;
		}

		default:	fatalerror("RDP: load_tlut: format = %d\n", ti_format);
	}

}

static void rdp_set_tile_size(UINT32 w1, UINT32 w2)
{
	int tilenum = (w2 >> 24) & 0x7;

	tile[tilenum].sl = (w1 >> 12) & 0xfff;
	tile[tilenum].tl = (w1 >>  0) & 0xfff;
	tile[tilenum].sh = (w2 >> 12) & 0xfff;
	tile[tilenum].th = (w2 >>  0) & 0xfff;
}

static void rdp_load_block(UINT32 w1, UINT32 w2)
{
	int i, width;
	UINT16 sl, sh, tl, dxt;
	int tilenum = (w2 >> 24) & 0x7;
	UINT32 *src, *tc;
	int tb;

	if (ti_format != tile[tilenum].format || ti_size != tile[tilenum].size)
		fatalerror("RDP: load_block: format conversion required!\n");

	sl	= ((w1 >> 12) & 0xfff);
	tl	= ((w1 >>  0) & 0xfff) << 11;
	sh	= ((w2 >> 12) & 0xfff);
	dxt	= ((w2 >>  0) & 0xfff);

	width = (sh - sl) + 1;

	switch (ti_size)
	{
		case PIXEL_SIZE_4BIT:	width >>= 1; break;
		case PIXEL_SIZE_8BIT:	break;
		case PIXEL_SIZE_16BIT:	width <<= 1; break;
		case PIXEL_SIZE_32BIT:	width <<= 2; break;
	}


	src = (UINT32*)&rdram[ti_address / 4];
	tc = (UINT32*)texture_cache;
	tb = tile[tilenum].tmem;

	if (dxt != 0)
	{
		int j=0;
		for (i=0; i < width; i+=2)
		{
			int t = j >> 11;

			tc[((tb+i) + 0)] = src[((((tl * ti_width) / 4) + sl + i + 0) ^ ((t & 1) ? 1 : 0))];
			tc[((tb+i) + 1)] = src[((((tl * ti_width) / 4) + sl + i + 1) ^ ((t & 1) ? 1 : 0))];

			j += dxt;
		}
	}
	else
	{
		for (i=0; i < width / 4; i++)
		{
			tc[(tb+i)] = src[((tl * ti_width) / 4) + sl + i];
		}
	}
}

static void rdp_load_tile(UINT32 w1, UINT32 w2)
{
	int i, j;
	UINT16 sl, sh, tl, th;
	int width, height;
	int tilenum = (w2 >> 24) & 0x7;

	if (ti_format != tile[tilenum].format || ti_size != tile[tilenum].size)
		fatalerror("RDP: load_block: format conversion required!\n");

	sl	= ((w1 >> 12) & 0xfff) / 4;
	tl	= ((w1 >>  0) & 0xfff) / 4;
	sh	= ((w2 >> 12) & 0xfff) / 4;
	th	= ((w2 >>  0) & 0xfff) / 4;

	width = (sh - sl) + 1;
	height = (th - tl) + 1;

	switch (ti_size)
	{
		case PIXEL_SIZE_8BIT:
		{
			UINT8 *src = (UINT8*)&rdram[ti_address / 4];
			UINT8 *tc = (UINT8*)texture_cache;
			int tb = tile[tilenum].tmem;

			if (tb + (width * height) > 4096)
			{
				fatalerror("rdp_load_tile 8-bit: tmem %04X, width %d, height %d = %d\n", tile[tilenum].tmem, width, height, width*height);
			}

			for (j=0; j < height; j++)
			{
				int tline = tb + (tile[tilenum].line * j);
				int s = ((j + tl) * ti_width) + sl;

				for (i=0; i < width; i++)
				{
					tc[((tline+i) ^ BYTE_ADDR_XOR) ^ ((j & 1) ? 4 : 0)] = src[(s++) ^ BYTE_ADDR_XOR];
				}
			}
			break;
		}
		case PIXEL_SIZE_16BIT:
		{
			UINT16 *src = (UINT16*)&rdram[ti_address / 4];
			UINT16 *tc = (UINT16*)texture_cache;
			int tb = (tile[tilenum].tmem / 2);

			if (tb + (width * height) > 2048)
			{
				//fatalerror("rdp_load_tile 16-bit: tmem %04X, width %d, height %d = %d\n", tile[tilenum].tmem, width, height, width*height);
			}

			for (j=0; j < height; j++)
			{
				int tline = tb + ((tile[tilenum].line / 2) * j);
				int s = ((j + tl) * ti_width) + sl;

				for (i=0; i < width; i++)
				{
					tc[((tline+i) ^ WORD_ADDR_XOR) ^ ((j & 1) ? 2 : 0)] = src[(s++) ^ WORD_ADDR_XOR];
				}
			}
			break;
		}
		case PIXEL_SIZE_32BIT:
		{
			UINT32 *src = (UINT32*)&rdram[ti_address / 4];
			UINT32 *tc = (UINT32*)texture_cache;
			int tb = (tile[tilenum].tmem / 4);

			if (tb + (width * height) > 1024)
			{
				fatalerror("rdp_load_tile 32-bit: tmem %04X, width %d, height %d = %d\n", tile[tilenum].tmem, width, height, width*height);
			}

			for (j=0; j < height; j++)
			{
				int tline = tb + ((tile[tilenum].line / 4) * j);
				int s = ((j + tl) * ti_width) + sl;

				for (i=0; i < width; i++)
				{
					tc[(tline+i) ^ ((j & 1) ? 1 : 0)] = src[(s++)];
				}
			}
			break;
		}

		default:	fatalerror("RDP: load_tile: size = %d\n", ti_size);
	}

}

static void rdp_set_tile(UINT32 w1, UINT32 w2)
{
	int tilenum = (w2 >> 24) & 0x7;

	tile[tilenum].format	= (w1 >> 21) & 0x7;
	tile[tilenum].size		= (w1 >> 19) & 0x3;
	tile[tilenum].line		= ((w1 >>  9) & 0x1ff) * 8;
	tile[tilenum].tmem		= ((w1 >>  0) & 0x1ff) * 8;
	tile[tilenum].ct		= (w2 >> 19) & 0x1;
	tile[tilenum].mt		= (w2 >> 18) & 0x1;
	tile[tilenum].mask_t	= (w2 >> 14) & 0xf;
	tile[tilenum].shift_t	= (w2 >> 10) & 0xf;
	tile[tilenum].cs		= (w2 >>  9) & 0x1;
	tile[tilenum].ms		= (w2 >>  8) & 0x1;
	tile[tilenum].mask_s	= (w2 >>  4) & 0xf;
	tile[tilenum].shift_s	= (w2 >>  0) & 0xf;

	// TODO: clamp & mirror parameters
}

static void rdp_fill_rect(UINT32 w1, UINT32 w2)
{
	RECTANGLE rect;
	rect.xl = (w1 >> 12) & 0xfff;
	rect.yl = (w1 >>  0) & 0xfff;
	rect.xh = (w2 >> 12) & 0xfff;
	rect.yh = (w2 >>  0) & 0xfff;

	switch (fb_size)
	{
		case PIXEL_SIZE_16BIT:		fill_rectangle_16bit(&rect); break;
		case PIXEL_SIZE_32BIT:		fill_rectangle_32bit(&rect); break;
	}
}

static void rdp_set_fill_color(UINT32 w1, UINT32 w2)
{
	fill_color = w2;
}

static void rdp_set_fog_color(UINT32 w1, UINT32 w2)
{
	fog_color.r = (w2 >> 24) & 0xff;
	fog_color.g = (w2 >> 16) & 0xff;
	fog_color.b = (w2 >>  8) & 0xff;
	fog_color.a = (w2 >>  0) & 0xff;
}

static void rdp_set_blend_color(UINT32 w1, UINT32 w2)
{
	blend_color.r = (w2 >> 24) & 0xff;
	blend_color.g = (w2 >> 16) & 0xff;
	blend_color.b = (w2 >>  8) & 0xff;
	blend_color.a = (w2 >>  0) & 0xff;
}

static void rdp_set_prim_color(UINT32 w1, UINT32 w2)
{
	// TODO: prim min level, prim_level
	prim_color.r = (w2 >> 24) & 0xff;
	prim_color.g = (w2 >> 16) & 0xff;
	prim_color.b = (w2 >>  8) & 0xff;
	prim_color.a = (w2 >>  0) & 0xff;
}

static void rdp_set_env_color(UINT32 w1, UINT32 w2)
{
	env_color.r = (w2 >> 24) & 0xff;
	env_color.g = (w2 >> 16) & 0xff;
	env_color.b = (w2 >>  8) & 0xff;
	env_color.a = (w2 >>  0) & 0xff;
}

static void rdp_set_combine(UINT32 w1, UINT32 w2)
{
	combine.sub_a_rgb0	= (w1 >> 20) & 0xf;
	combine.mul_rgb0	= (w1 >> 15) & 0x1f;
	combine.sub_a_a0	= (w1 >> 12) & 0x7;
	combine.mul_a0		= (w1 >>  9) & 0x7;
	combine.sub_a_rgb1	= (w1 >>  5) & 0xf;
	combine.mul_rgb1	= (w1 >>  0) & 0x1f;

	combine.sub_b_rgb0	= (w2 >> 28) & 0xf;
	combine.sub_b_rgb1	= (w2 >> 24) & 0xf;
	combine.sub_a_a1	= (w2 >> 21) & 0x7;
	combine.mul_a1		= (w2 >> 18) & 0x7;
	combine.add_rgb0	= (w2 >> 15) & 0x7;
	combine.sub_b_a0	= (w2 >> 12) & 0x7;
	combine.add_a0		= (w2 >>  9) & 0x7;
	combine.add_rgb1	= (w2 >>  6) & 0x7;
	combine.sub_b_a1	= (w2 >>  3) & 0x7;
	combine.add_a1		= (w2 >>  0) & 0x7;

	SET_SUBA_RGB_INPUT(&combiner_rgbsub_a_r[0], &combiner_rgbsub_a_g[0], &combiner_rgbsub_a_b[0], combine.sub_a_rgb0);
	SET_SUBB_RGB_INPUT(&combiner_rgbsub_b_r[0], &combiner_rgbsub_b_g[0], &combiner_rgbsub_b_b[0], combine.sub_b_rgb0);
	SET_MUL_RGB_INPUT(&combiner_rgbmul_r[0], &combiner_rgbmul_g[0], &combiner_rgbmul_b[0], combine.mul_rgb0);
	SET_ADD_RGB_INPUT(&combiner_rgbadd_r[0], &combiner_rgbadd_g[0], &combiner_rgbadd_b[0], combine.add_rgb0);
	SET_SUB_ALPHA_INPUT(&combiner_alphasub_a[0], combine.sub_a_a0);
	SET_SUB_ALPHA_INPUT(&combiner_alphasub_b[0], combine.sub_b_a0);
	SET_MUL_ALPHA_INPUT(&combiner_alphamul[0], combine.mul_a0);
	SET_SUB_ALPHA_INPUT(&combiner_alphaadd[0], combine.add_a0);

	SET_SUBA_RGB_INPUT(&combiner_rgbsub_a_r[1], &combiner_rgbsub_a_g[1], &combiner_rgbsub_a_b[1], combine.sub_a_rgb1);
	SET_SUBB_RGB_INPUT(&combiner_rgbsub_b_r[1], &combiner_rgbsub_b_g[1], &combiner_rgbsub_b_b[1], combine.sub_b_rgb1);
	SET_MUL_RGB_INPUT(&combiner_rgbmul_r[1], &combiner_rgbmul_g[1], &combiner_rgbmul_b[1], combine.mul_rgb1);
	SET_ADD_RGB_INPUT(&combiner_rgbadd_r[1], &combiner_rgbadd_g[1], &combiner_rgbadd_b[1], combine.add_rgb1);
	SET_SUB_ALPHA_INPUT(&combiner_alphasub_a[1], combine.sub_a_a1);
	SET_SUB_ALPHA_INPUT(&combiner_alphasub_b[1], combine.sub_b_a1);
	SET_MUL_ALPHA_INPUT(&combiner_alphamul[1], combine.mul_a1);
	SET_SUB_ALPHA_INPUT(&combiner_alphaadd[1], combine.add_a1);
}

static void rdp_set_texture_image(UINT32 w1, UINT32 w2)
{
	ti_format	= (w1 >> 21) & 0x7;
	ti_size		= (w1 >> 19) & 0x3;
	ti_width	= (w1 & 0x3ff) + 1;
	ti_address	= w2 & 0x01ffffff;
}

static void rdp_set_mask_image(UINT32 w1, UINT32 w2)
{
	zb_address	= w2 & 0x01ffffff;
}

static void rdp_set_color_image(UINT32 w1, UINT32 w2)
{
	fb_format 	= (w1 >> 21) & 0x7;
	fb_size		= (w1 >> 19) & 0x3;
	fb_width	= (w1 & 0x3ff) + 1;
	fb_address	= w2 & 0x01ffffff;

	//if (fb_format != 0)   fatalerror("rdp_set_color_image: format = %d\n", fb_format);
	if (fb_format != 0) fb_format = 0;
}

/*****************************************************************************/

static void (* rdp_command_table[64])(UINT32 w1, UINT32 w2) =
{
	/* 0x00 */
	rdp_noop,			rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_tri_noshade,	rdp_tri_noshade_z,		rdp_tri_tex,			rdp_tri_tex_z,
	rdp_tri_shade,		rdp_tri_shade_z,		rdp_tri_texshade,		rdp_tri_texshade_z,
	/* 0x10 */
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	/* 0x20 */
	rdp_invalid,		rdp_invalid,			rdp_invalid,			rdp_invalid,
	rdp_tex_rect,		rdp_tex_rect_flip,		rdp_sync_load,			rdp_sync_pipe,
	rdp_sync_tile,		rdp_sync_full,			rdp_set_key_gb,			rdp_set_key_r,
	rdp_set_convert,	rdp_set_scissor,		rdp_set_prim_depth,		rdp_set_other_modes,
	/* 0x30 */
	rdp_load_tlut,		rdp_invalid,			rdp_set_tile_size,		rdp_load_block,
	rdp_load_tile,		rdp_set_tile,			rdp_fill_rect,			rdp_set_fill_color,
	rdp_set_fog_color,	rdp_set_blend_color,	rdp_set_prim_color,		rdp_set_env_color,
	rdp_set_combine,	rdp_set_texture_image,	rdp_set_mask_image,		rdp_set_color_image
};

void rdp_process_list(void)
{
	int i;
	UINT32 cmd, length, cmd_length;

  if (dp_end <= dp_current) return;
	length = dp_end - dp_current;

	// load command data
	for (i=0; i < length; i += 4)
	{
		rdp_cmd_data[rdp_cmd_ptr++] = READ_RDP_DATA(dp_current + i);
		if (rdp_cmd_ptr >= 0x1000)
		{
			printf("rdp_process_list: rdp_cmd_ptr overflow %x %x --> %x\n", length, dp_current, dp_end);
		}
	}

	dp_current = dp_end;

	cmd = (rdp_cmd_data[0] >> 24) & 0x3f;
	cmd_length = (rdp_cmd_ptr + 1) * 4;

	// check if more data is needed
	if (cmd_length < rdp_command_length[cmd])
	{
		return;
	}

	while (rdp_cmd_cur < rdp_cmd_ptr)
	{
		cmd = (rdp_cmd_data[rdp_cmd_cur] >> 24) & 0x3f;
	//  if (((rdp_cmd_data[rdp_cmd_cur] >> 24) & 0xc0) != 0xc0)
	//  {
	//      fatalerror("rdp_process_list: invalid rdp command %08X at %08X\n", rdp_cmd_data[rdp_cmd_cur], dp_start+(rdp_cmd_cur * 4));
	//  }

		if (((rdp_cmd_ptr-rdp_cmd_cur) * 4) < rdp_command_length[cmd])
		{
			return;
			//fatalerror("rdp_process_list: not enough rdp command data: cur = %d, ptr = %d, expected = %d\n", rdp_cmd_cur, rdp_cmd_ptr, rdp_command_length[cmd]);
		}

#if LOG_RDP_EXECUTION
		{
			char string[4000];
			rdp_dasm(string);

			fprintf(rdp_exec, "%08X: %08X %08X   %s\n", dp_start+(rdp_cmd_cur * 4), rdp_cmd_data[rdp_cmd_cur+0], rdp_cmd_data[rdp_cmd_cur+1], string);
		}
#endif

		// execute the command
		rdp_command_table[cmd](rdp_cmd_data[rdp_cmd_cur+0], rdp_cmd_data[rdp_cmd_cur+1]);

		rdp_cmd_cur += rdp_command_length[cmd] / 4;
	};
	rdp_cmd_ptr = 0;
	rdp_cmd_cur = 0;

	dp_start = dp_end;

  dp_status &= ~0x0002;
}
